diff --git a/plugins/xcmetrics/src/components/OverviewComponent/OverviewComponent.tsx b/plugins/xcmetrics/src/components/OverviewComponent/OverviewComponent.tsx index 3cb8b69be0..cee8e2a549 100644 --- a/plugins/xcmetrics/src/components/OverviewComponent/OverviewComponent.tsx +++ b/plugins/xcmetrics/src/components/OverviewComponent/OverviewComponent.tsx @@ -31,7 +31,7 @@ import { Build, BuildStatus, xcmetricsApiRef } from '../../api'; import { useAsync } from 'react-use'; import { Alert } from '@material-ui/lab'; import { StatusMatrixComponent } from '../StatusMatrixComponent'; -import { formatTime } from '../../utils'; +import { formatDuration, formatTime } from '../../utils'; import { Chip, Grid } from '@material-ui/core'; import { OverviewTrendsComponent } from '../OverviewTrendsComponent'; @@ -60,6 +60,11 @@ const columns: TableColumn[] = [ searchable: false, render: data => formatTime(data.startTimestamp), }, + { + title: 'Duration', + field: 'duration', + render: data => formatDuration(data.duration), + }, { title: 'User', field: 'userid', @@ -101,7 +106,7 @@ export const OverviewComponent = () => { Dashboard for XCMetrics - + { } /> - + - + diff --git a/plugins/xcmetrics/src/components/OverviewTrendsComponent/OverviewTrendsComponent.test.tsx b/plugins/xcmetrics/src/components/OverviewTrendsComponent/OverviewTrendsComponent.test.tsx index a25f64c685..ddde3952b3 100644 --- a/plugins/xcmetrics/src/components/OverviewTrendsComponent/OverviewTrendsComponent.test.tsx +++ b/plugins/xcmetrics/src/components/OverviewTrendsComponent/OverviewTrendsComponent.test.tsx @@ -19,6 +19,7 @@ import { renderInTestApp } from '@backstage/test-utils'; import { xcmetricsApiRef } from '../../api'; import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; import { createMockXcmetricsApi } from '../../test-utils'; +import userEvent from '@testing-library/user-event'; describe('OverviewTrendsComponent', () => { it('should render', async () => { @@ -26,10 +27,10 @@ describe('OverviewTrendsComponent', () => { - + , ); - expect(rendered.getByText('Last 14 Days')).toBeInTheDocument(); + expect(rendered.getByText('Trends for')).toBeInTheDocument(); expect(rendered.getAllByText('Build Count').length).toEqual(3); expect(rendered.getByText('Avg. Build Time (P50)')).toBeInTheDocument(); }); @@ -40,12 +41,26 @@ describe('OverviewTrendsComponent', () => { const rendered = await renderInTestApp( - + , ); expect(rendered.getByText('--')).toBeInTheDocument(); }); + it('should change number of days when select is changed', async () => { + const rendered = await renderInTestApp( + + + , + ); + + userEvent.click(rendered.getByText('14 days')); + userEvent.click(await rendered.findByText('30 days')); + expect(await rendered.findByText('30 days')).toBeInTheDocument(); + }); + it('should show errors when API not responding', async () => { const api = createMockXcmetricsApi(); const buildCountError = 'MockBuildCountErrorMessage'; @@ -60,7 +75,7 @@ describe('OverviewTrendsComponent', () => { const rendered = await renderInTestApp( - + , ); expect(rendered.getByText(buildCountError)).toBeInTheDocument(); diff --git a/plugins/xcmetrics/src/components/OverviewTrendsComponent/OverviewTrendsComponent.tsx b/plugins/xcmetrics/src/components/OverviewTrendsComponent/OverviewTrendsComponent.tsx index a7f46d1851..2c4a04d582 100644 --- a/plugins/xcmetrics/src/components/OverviewTrendsComponent/OverviewTrendsComponent.tsx +++ b/plugins/xcmetrics/src/components/OverviewTrendsComponent/OverviewTrendsComponent.tsx @@ -13,68 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Grid, makeStyles, Typography, useTheme } from '@material-ui/core'; -import React from 'react'; -import { Progress } from '@backstage/core-components'; +import { Grid, makeStyles, useTheme } from '@material-ui/core'; +import React, { useState } from 'react'; +import { Progress, Select } from '@backstage/core-components'; import { TrendComponent } from '../TrendComponent'; import { Alert, AlertTitle } from '@material-ui/lab'; -import { BuildCount, BuildTime, xcmetricsApiRef } from '../../api'; +import { xcmetricsApiRef } from '../../api'; import { useAsync } from 'react-use'; import { useApi } from '@backstage/core-plugin-api'; import { DataValueGridItem } from '../DataValueComponent'; -import { formatDuration, formatPercentage } from '../../utils'; +import { + formatDuration, + formatPercentage, + getAverageDuration, + getErrorRatios, + getValues, + sumField, +} from '../../utils'; import { BackstageTheme } from '@backstage/theme'; -const getErrorRatios = (buildCounts?: BuildCount[]) => { - if (!buildCounts?.length) { - return undefined; - } - - return buildCounts.map(counts => - counts.builds === 0 ? 0 : counts.errors / counts.builds, - ); -}; - -const getBuildCounts = (buildCounts?: BuildCount[]) => { - if (!buildCounts?.length) { - return undefined; - } - - return buildCounts.map(counts => counts.builds); -}; - -const getBuildDurationsP50 = (buildTimes?: BuildTime[]) => { - if (!buildTimes?.length) { - return undefined; - } - - return buildTimes.map(times => times.durationP50); -}; - -const getAverageDuration = ( - buildTimes: BuildTime[] | undefined, - accessor: (b: BuildTime) => number, -) => { - if (!buildTimes?.length) { - return undefined; - } - - return formatDuration( - buildTimes.reduce((sum, current) => sum + accessor(current), 0) / - buildTimes.length, - ); -}; - -const getTotalBuildDuration = (buildTimes?: BuildTime[]) => { - if (!buildTimes?.length) { - return undefined; - } - - return formatDuration( - buildTimes.reduce((sum, current) => sum + current.totalDuration, 0), - ); -}; - const useStyles = makeStyles({ spacingTop: { marginTop: 8, @@ -85,30 +42,31 @@ const useStyles = makeStyles({ }, }); -export const OverviewTrendsComponent = ({ days }: { days: number }) => { +const DAYS_SELECT_ITEMS = [ + { label: '7 days', value: 7 }, + { label: '14 days', value: 14 }, + { label: '30 days', value: 30 }, + { label: '60 days', value: 60 }, +]; + +export const OverviewTrendsComponent = () => { + const [days, setDays] = useState(14); const theme = useTheme(); const classes = useStyles(); const client = useApi(xcmetricsApiRef); - const buildCountsResult = useAsync( - async () => client.getBuildCounts(days), - [], - ); - const buildTimesResult = useAsync(async () => client.getBuildTimes(days), []); + const buildCountsResult = useAsync(async () => client.getBuildCounts(days), [ + days, + ]); + const buildTimesResult = useAsync(async () => client.getBuildTimes(days), [ + days, + ]); if (buildCountsResult.loading && buildTimesResult.loading) { return ; } - const sumBuilds = buildCountsResult.value?.reduce( - (sum, current) => sum + current.builds, - 0, - ); - - const sumErrors = buildCountsResult.value?.reduce( - (sum, current) => sum + current.errors, - 0, - ); - + const sumBuilds = sumField(b => b.builds, buildCountsResult.value); + const sumErrors = sumField(b => b.errors, buildCountsResult.value); const errorRate = sumBuilds && sumErrors ? sumErrors / sumBuilds : undefined; const averageBuildDurationP50 = getAverageDuration( @@ -119,11 +77,16 @@ export const OverviewTrendsComponent = ({ days }: { days: number }) => { buildTimesResult.value, b => b.durationP95, ); - const totalBuildTime = getTotalBuildDuration(buildTimesResult.value); + const totalBuildTime = sumField(t => t.totalDuration, buildTimesResult.value); return ( <> - Last {days} Days +