зеркало из https://github.com/mozilla/treeherder.git
Bug 1748645 - Tests View - Link from the number of alerts to the alerts view for that test (#7355)
* Bug 1748645 - Add link to improvement alerts * Bug 1748645 - Add link to regression alerts * Bug 1748645 - fetch alerts for regression link * Bug 1748645 - add unit tests * Bug 1748645 - Fix filterText string * Bug 1748645 - Address PR request * Bug 1748645 - fix improvements count * Bug 1748645 - Count untriaged alerts, display the number with link * Bug 1748645 - Add notes for improvement and regression tooltip
This commit is contained in:
Родитель
40de4922ad
Коммит
79ede943b2
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import { render, cleanup, waitFor, fireEvent } from '@testing-library/react';
|
||||
import { MemoryRouter as Router } from 'react-router-dom';
|
||||
|
||||
import { noResultsMessage } from '../../../ui/perfherder/perf-helpers/constants';
|
||||
import TestsTable from '../../../ui/perfherder/tests/TestsTable';
|
||||
|
@ -35,13 +36,24 @@ const platformsMap = {
|
|||
2: 'platform2',
|
||||
};
|
||||
|
||||
const activeFramework = 'awsy';
|
||||
|
||||
const allFrameworks = [
|
||||
{ id: 1, name: 'talos' },
|
||||
{ id: 4, name: 'awsy' },
|
||||
];
|
||||
|
||||
const testsTable = (data, projectsMap = false, platformsMap = false) =>
|
||||
render(
|
||||
<TestsTable
|
||||
results={data}
|
||||
projectsMap={projectsMap}
|
||||
platformsMap={platformsMap}
|
||||
/>,
|
||||
<Router>
|
||||
<TestsTable
|
||||
results={data}
|
||||
projectsMap={projectsMap}
|
||||
platformsMap={platformsMap}
|
||||
allFrameworks={allFrameworks}
|
||||
framework={activeFramework}
|
||||
/>
|
||||
</Router>,
|
||||
);
|
||||
|
||||
afterEach(cleanup);
|
||||
|
@ -99,3 +111,21 @@ test('Test alerts from Alerts column are split into improvements and regressions
|
|||
expect(regressions[0]).toBeInTheDocument();
|
||||
expect(regressions[0]).toHaveTextContent('100');
|
||||
});
|
||||
|
||||
test('Improvement alerts number has the corresponding link', async () => {
|
||||
const { getAllByTestId } = testsTable(results, projectsMap, platformsMap);
|
||||
|
||||
const improvements = await waitFor(() => getAllByTestId('improvements'));
|
||||
|
||||
const link = `/alerts?hideDwnToInv=0&filterText=Base Content Explicit+test1&page=1&status=4&framework=4`;
|
||||
expect(improvements[0].children[0]).toHaveAttribute('href', link);
|
||||
});
|
||||
|
||||
test('Regression alerts number has the corresponding link', async () => {
|
||||
const { getAllByTestId } = testsTable(results, projectsMap, platformsMap);
|
||||
|
||||
const regressions = await waitFor(() => getAllByTestId('regressions'));
|
||||
|
||||
const link = `/alerts?hideDwnToInv=0&filterText=Base Content Explicit+test1&page=1&status=9&framework=4`;
|
||||
expect(regressions[0].children[0]).toHaveAttribute('href', link);
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@ from collections import defaultdict
|
|||
import django_filters
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from django.db.models import CharField, Count, Q, Subquery, Value
|
||||
from django.db.models import CharField, Count, Q, Subquery, Value, Case, When
|
||||
from django.db.models.functions import Concat
|
||||
from rest_framework import exceptions, filters, generics, pagination, viewsets
|
||||
from rest_framework.response import Response
|
||||
|
@ -749,7 +749,16 @@ class TestSuiteHealthViewSet(viewsets.ViewSet):
|
|||
.annotate(repositories=GroupConcat('repository_id', distinct=True))
|
||||
.annotate(platforms=GroupConcat('platform_id', distinct=True))
|
||||
.annotate(total_alerts=Count('performancealert'))
|
||||
.annotate(total_regressions=Count('performancealert__is_regression'))
|
||||
.annotate(
|
||||
total_regressions=Count(
|
||||
Case(When(performancealert__is_regression=1, then=Value(1)))
|
||||
)
|
||||
)
|
||||
.annotate(
|
||||
total_untriaged=Count(
|
||||
Case(When(performancealert__status=PerformanceAlert.UNTRIAGED, then=Value(1)))
|
||||
)
|
||||
)
|
||||
.order_by('suite', 'test')
|
||||
)
|
||||
|
||||
|
|
|
@ -401,3 +401,4 @@ class TestSuiteHealthSerializer(serializers.Serializer):
|
|||
repositories = CommaSeparatedField()
|
||||
total_alerts = serializers.IntegerField()
|
||||
total_regressions = serializers.IntegerField()
|
||||
total_untriaged = serializers.IntegerField()
|
||||
|
|
|
@ -262,6 +262,10 @@ class AlertsView extends React.Component {
|
|||
if (filterText) {
|
||||
params.filter_text = filterText;
|
||||
}
|
||||
if (status === 'all regressions') {
|
||||
delete params.status;
|
||||
params.hide_improvements = true;
|
||||
}
|
||||
if (hideDownstream) {
|
||||
params.hide_related_and_invalid = hideDownstream;
|
||||
}
|
||||
|
|
|
@ -83,6 +83,7 @@ export const summaryStatusMap = {
|
|||
wontfix: 6,
|
||||
fixed: 7,
|
||||
backedout: 8,
|
||||
'all regressions': 9,
|
||||
};
|
||||
|
||||
export const alertStatusMap = {
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import SimpleTooltip from '../../shared/SimpleTooltip';
|
||||
import { summaryStatusMap } from '../perf-helpers/constants';
|
||||
|
||||
export default class AlertsLink extends React.PureComponent {
|
||||
getTooltip = (type) => {
|
||||
return (
|
||||
<>
|
||||
{type.charAt(0).toUpperCase() + type.slice(1)} <br />
|
||||
<i>Note: Untriaged {type} can be seen through untriaged link</i>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { alerts, framework: frameworkName, allFrameworks } = this.props;
|
||||
const {
|
||||
total_alerts: totalAlerts,
|
||||
total_regressions: regressions,
|
||||
total_untriaged: untriaged,
|
||||
suite,
|
||||
test,
|
||||
} = alerts;
|
||||
const improvements = totalAlerts - regressions;
|
||||
const framework = allFrameworks.find((item) => item.name === frameworkName);
|
||||
|
||||
const filterText = suite && test ? suite.concat(`+${test}`) : suite || test;
|
||||
return (
|
||||
<div className="d-flex justify-content-center">
|
||||
<div data-testid="improvements" className="w-50">
|
||||
<Link
|
||||
to={`./alerts?hideDwnToInv=0&filterText=${filterText}&page=1&status=${summaryStatusMap.improvement}&framework=${framework.id}`}
|
||||
target="_blank"
|
||||
>
|
||||
<SimpleTooltip
|
||||
text={`${improvements || 0}`}
|
||||
tooltipText={this.getTooltip('improvements')}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div data-testid="regressions" className="w-50">
|
||||
<Link
|
||||
to={`./alerts?hideDwnToInv=0&filterText=${filterText}&page=1&status=${summaryStatusMap['all regressions']}&framework=${framework.id}`}
|
||||
target="_blank"
|
||||
>
|
||||
<SimpleTooltip
|
||||
text={`${regressions || 0}`}
|
||||
tooltipText={this.getTooltip('regressions')}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div data-testid="untriaged" className="w-50">
|
||||
<Link
|
||||
to={`./alerts?hideDwnToInv=0&filterText=${filterText}&page=1&status=${summaryStatusMap.untriaged}&framework=${framework.id}`}
|
||||
target="_blank"
|
||||
>
|
||||
<SimpleTooltip text={`${untriaged || 0}`} tooltipText="Untriaged" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AlertsLink.propTypes = {
|
||||
alerts: PropTypes.shape({}).isRequired,
|
||||
};
|
|
@ -1,35 +0,0 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import SimpleTooltip from '../../shared/SimpleTooltip';
|
||||
|
||||
export default class AlertsList extends React.PureComponent {
|
||||
render() {
|
||||
const { alerts } = this.props;
|
||||
const {
|
||||
total_alerts: totalAlerts,
|
||||
total_regressions: regressions,
|
||||
} = alerts;
|
||||
const improvements = totalAlerts - regressions;
|
||||
return (
|
||||
<div className="d-flex justify-content-around">
|
||||
<div data-testid="improvements">
|
||||
<SimpleTooltip
|
||||
text={`${improvements || 0}`}
|
||||
tooltipText="Improvements"
|
||||
/>
|
||||
</div>
|
||||
<div data-testid="regressions">
|
||||
<SimpleTooltip
|
||||
text={`${regressions || 0}`}
|
||||
tooltipText="Regressions"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AlertsList.propTypes = {
|
||||
alerts: PropTypes.shape({}).isRequired,
|
||||
};
|
|
@ -8,12 +8,13 @@ import { Perfdocs, perfViews } from '../perf-helpers/perfdocs';
|
|||
|
||||
import ItemList from './ItemList';
|
||||
import PlatformList from './PlatformList';
|
||||
import AlertsList from './AlertsList';
|
||||
import AlertsLink from './AlertsLink';
|
||||
|
||||
export default function TestsTable(props) {
|
||||
const {
|
||||
results,
|
||||
framework,
|
||||
allFrameworks,
|
||||
projectsMap,
|
||||
platformsMap,
|
||||
defaultPageSize,
|
||||
|
@ -93,7 +94,13 @@ export default function TestsTable(props) {
|
|||
accessor: 'total_alerts',
|
||||
Cell: (props) => {
|
||||
const { original } = props;
|
||||
return <AlertsList alerts={original} />;
|
||||
return (
|
||||
<AlertsLink
|
||||
alerts={original}
|
||||
framework={framework}
|
||||
allFrameworks={allFrameworks}
|
||||
/>
|
||||
);
|
||||
},
|
||||
width: 100,
|
||||
style: { textAlign: 'center' },
|
||||
|
|
|
@ -57,7 +57,12 @@ export default class TestsTableControls extends React.Component {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { dropdownOptions, projectsMap, platformsMap } = this.props;
|
||||
const {
|
||||
dropdownOptions,
|
||||
projectsMap,
|
||||
platformsMap,
|
||||
allFrameworks,
|
||||
} = this.props;
|
||||
const { results } = this.state;
|
||||
let framework = false;
|
||||
if (dropdownOptions[0] !== undefined)
|
||||
|
@ -73,6 +78,7 @@ export default class TestsTableControls extends React.Component {
|
|||
<TestsTable
|
||||
results={results}
|
||||
framework={framework}
|
||||
allFrameworks={allFrameworks}
|
||||
projectsMap={projectsMap}
|
||||
platformsMap={platformsMap}
|
||||
/>
|
||||
|
|
|
@ -174,6 +174,7 @@ class TestsView extends React.PureComponent {
|
|||
dropdownOptions={dropdowns}
|
||||
projectsMap={projectsMap}
|
||||
platformsMap={platformsMap}
|
||||
allFrameworks={frameworks}
|
||||
/>
|
||||
</Container>
|
||||
</ErrorBoundary>
|
||||
|
|
Загрузка…
Ссылка в новой задаче