Skip to content

Commit 09469b3

Browse files
committed
Sort service list by TPM if health is not shown (elastic#80447)
Fall back to sorting by transactions per minute if the health column is not available. Update the test for the component by moving it, removing snapshots, converting to React Testing Library, converting to TypeScript, and adding new cases for this sorting logic. Fixes elastic#79827.
1 parent 9110a83 commit 09469b3

File tree

5 files changed

+127
-239
lines changed

5 files changed

+127
-239
lines changed

x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/props.json renamed to x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__fixtures__/props.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,7 @@
1111
"value": 46.06666666666667,
1212
"timeseries": []
1313
},
14-
"avgResponseTime": null,
15-
"environments": [
16-
"test"
17-
]
14+
"environments": ["test"]
1815
},
1916
{
2017
"serviceName": "opbeans-python",

x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/List.test.js

Lines changed: 0 additions & 80 deletions
This file was deleted.

x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/__snapshots__/List.test.js.snap

Lines changed: 0 additions & 153 deletions
This file was deleted.

x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,18 +191,20 @@ export function ServiceList({ items, noItemsMessage }: Props) {
191191
const columns = displayHealthStatus
192192
? SERVICE_COLUMNS
193193
: SERVICE_COLUMNS.filter((column) => column.field !== 'healthStatus');
194+
const initialSortField = displayHealthStatus
195+
? 'healthStatus'
196+
: 'transactionsPerMinute';
194197

195198
return (
196199
<ManagedTable
197200
columns={columns}
198201
items={items}
199202
noItemsMessage={noItemsMessage}
200-
initialSortField="healthStatus"
203+
initialSortField={initialSortField}
201204
initialSortDirection="desc"
202205
initialPageSize={50}
203206
sortFn={(itemsToSort, sortField, sortDirection) => {
204207
// For healthStatus, sort items by healthStatus first, then by TPM
205-
206208
return sortField === 'healthStatus'
207209
? orderBy(
208210
itemsToSort,
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import React, { ReactNode } from 'react';
8+
import { MemoryRouter } from 'react-router-dom';
9+
import { ServiceHealthStatus } from '../../../../../common/service_health_status';
10+
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
11+
import { ServiceListAPIResponse } from '../../../../../server/lib/services/get_services';
12+
import { MockApmPluginContextWrapper } from '../../../../context/ApmPluginContext/MockApmPluginContext';
13+
import { mockMoment, renderWithTheme } from '../../../../utils/testHelpers';
14+
import { ServiceList, SERVICE_COLUMNS } from './';
15+
import props from './__fixtures__/props.json';
16+
17+
function Wrapper({ children }: { children?: ReactNode }) {
18+
return (
19+
<MockApmPluginContextWrapper>
20+
<MemoryRouter>{children}</MemoryRouter>
21+
</MockApmPluginContextWrapper>
22+
);
23+
}
24+
25+
describe('ServiceList', () => {
26+
beforeAll(() => {
27+
mockMoment();
28+
});
29+
30+
it('renders empty state', () => {
31+
expect(() =>
32+
renderWithTheme(<ServiceList items={[]} />, { wrapper: Wrapper })
33+
).not.toThrowError();
34+
});
35+
36+
it('renders with data', () => {
37+
expect(() =>
38+
// Types of property 'avgResponseTime' are incompatible.
39+
// Type 'null' is not assignable to type '{ value: number | null; timeseries: { x: number; y: number | null; }[]; } | undefined'.ts(2322)
40+
renderWithTheme(
41+
<ServiceList items={props.items as ServiceListAPIResponse['items']} />,
42+
{ wrapper: Wrapper }
43+
)
44+
).not.toThrowError();
45+
});
46+
47+
it('renders columns correctly', () => {
48+
const service: any = {
49+
serviceName: 'opbeans-python',
50+
agentName: 'python',
51+
transactionsPerMinute: {
52+
value: 86.93333333333334,
53+
timeseries: [],
54+
},
55+
errorsPerMinute: {
56+
value: 12.6,
57+
timeseries: [],
58+
},
59+
avgResponseTime: {
60+
value: 91535.42944785276,
61+
timeseries: [],
62+
},
63+
environments: ['test'],
64+
};
65+
const renderedColumns = SERVICE_COLUMNS.map((c) =>
66+
c.render!(service[c.field!], service)
67+
);
68+
69+
expect(renderedColumns[0]).toMatchInlineSnapshot(`
70+
<HealthBadge
71+
healthStatus="unknown"
72+
/>
73+
`);
74+
});
75+
76+
describe('without ML data', () => {
77+
it('does not render the health column', () => {
78+
const { queryByText } = renderWithTheme(
79+
<ServiceList items={props.items as ServiceListAPIResponse['items']} />,
80+
{
81+
wrapper: Wrapper,
82+
}
83+
);
84+
const healthHeading = queryByText('Health');
85+
86+
expect(healthHeading).toBeNull();
87+
});
88+
89+
it('sorts by transactions per minute', async () => {
90+
const { findByTitle } = renderWithTheme(
91+
<ServiceList items={props.items as ServiceListAPIResponse['items']} />,
92+
{
93+
wrapper: Wrapper,
94+
}
95+
);
96+
97+
expect(
98+
await findByTitle('Trans. per minute; Sorted in descending order')
99+
).toBeInTheDocument();
100+
});
101+
});
102+
103+
describe('with ML data', () => {
104+
it('renders the health column', async () => {
105+
const { findByTitle } = renderWithTheme(
106+
<ServiceList
107+
items={(props.items as ServiceListAPIResponse['items']).map(
108+
(item) => ({
109+
...item,
110+
healthStatus: ServiceHealthStatus.warning,
111+
})
112+
)}
113+
/>,
114+
{ wrapper: Wrapper }
115+
);
116+
117+
expect(
118+
await findByTitle('Health; Sorted in descending order')
119+
).toBeInTheDocument();
120+
});
121+
});
122+
});

0 commit comments

Comments
 (0)