зеркало из https://github.com/mozilla/treeherder.git
Bug 1735489 - Show other alerts when opening graph view (#7296)
* Bug 1735489 - Fetch other alerts from backend based on framework id * Bug 1735489 - Add highlight other alerts toggle, add vertical lines to mark other alerts * Bug 1735489 - Add unit test, clean code * Bug 1735489 - Address pr requests * Bug 1735489 - Fix failing test
This commit is contained in:
Родитель
b4b7fa86a6
Коммит
3b4504dd0f
|
@ -0,0 +1,124 @@
|
|||
[
|
||||
[
|
||||
{
|
||||
"id": 32205,
|
||||
"push_id": 980228,
|
||||
"prev_push_id": 980227,
|
||||
"created": "2021-11-01T11:36:53.407841",
|
||||
"repository": "autoland",
|
||||
"framework": 1,
|
||||
"alerts": [
|
||||
{
|
||||
"id": 137494,
|
||||
"status": 0,
|
||||
"series_signature": {
|
||||
"id": 3184996,
|
||||
"framework_id": 1,
|
||||
"signature_hash": "ecac3c7a967df71884a23d925a49e8e96fd21c21",
|
||||
"machine_platform": "macosx1015-64-shippable-qr",
|
||||
"suite": "perf_reftest_singletons",
|
||||
"test": "window-named-property-get.html",
|
||||
"lower_is_better": true,
|
||||
"has_subtests": false,
|
||||
"option_collection_hash": "102210fe594ee9b33d82058545b1ed14f4c8206e",
|
||||
"tags": [""],
|
||||
"extra_options": ["e10s", "stylo", "webrender"],
|
||||
"measurement_unit": "ms",
|
||||
"suite_public_name": null,
|
||||
"test_public_name": null
|
||||
},
|
||||
"is_regression": false,
|
||||
"prev_value": 723.69,
|
||||
"new_value": 649.57,
|
||||
"t_value": 7.01,
|
||||
"amount_abs": -74.12,
|
||||
"amount_pct": 10.24,
|
||||
"summary_id": 32205,
|
||||
"related_summary_id": null,
|
||||
"manually_created": false,
|
||||
"classifier": null,
|
||||
"starred": false,
|
||||
"classifier_email": null,
|
||||
"backfill_record": {
|
||||
"context": "[{\"perf_datum_id\": 1437239345, \"value\": 703.62, \"job_id\": 356253496, \"push_id\": 980182, \"push_timestamp\": \"2021-10-27T19:02:20\", \"push__revision\": \"c24a073833415c904a810f0bc14de21ed8f04def\"}, {\"perf_datum_id\": 1438848232, \"value\": 752.3, \"job_id\": 356594861, \"push_id\": 980227, \"push_timestamp\": \"2021-10-27T20:48:35\", \"push__revision\": \"197af0b52aec261be5f484b48c46c26c8f208ae2\"}, {\"perf_datum_id\": 1438845794, \"value\": 637.2, \"job_id\": 356594352, \"push_id\": 980228, \"push_timestamp\": \"2021-10-27T20:49:39\", \"push__revision\": \"ff38beee092bcdfcc289f4737668c9b5d4ff3d42\"}, {\"perf_datum_id\": 1438591078, \"value\": 700.56, \"job_id\": 356552835, \"push_id\": 980244, \"push_timestamp\": \"2021-10-27T21:17:00\", \"push__revision\": \"dd99452b671854d9f1f3079a051fc4822161a993\"}, {\"perf_datum_id\": 1438585688, \"value\": 635.5699999999999, \"job_id\": 356550571, \"push_id\": 980255, \"push_timestamp\": \"2021-10-27T21:43:08\", \"push__revision\": \"cb160c184b4ac2b4701688e825514f8a3add6a7e\"}]",
|
||||
"status": 0,
|
||||
"total_backfills_triggered": 0
|
||||
},
|
||||
"noise_profile": "N/A"
|
||||
}
|
||||
],
|
||||
"related_alerts": [],
|
||||
"status": 0,
|
||||
"bug_number": null,
|
||||
"bug_updated": null,
|
||||
"issue_tracker": 1,
|
||||
"notes": null,
|
||||
"revision": "2909b0a1eb06cc34ce0a11544e5e6826aba87c06",
|
||||
"push_timestamp": 1635367779,
|
||||
"prev_push_revision": "197af0b52aec261be5f484b48c46c26c8f208ae2",
|
||||
"assignee_username": null,
|
||||
"assignee_email": null,
|
||||
"performance_tags": []
|
||||
},
|
||||
{
|
||||
"id": 32204,
|
||||
"push_id": 980323,
|
||||
"prev_push_id": 980306,
|
||||
"created": "2021-11-01T11:31:11.031842",
|
||||
"repository": "autoland",
|
||||
"framework": 1,
|
||||
"alerts": [
|
||||
{
|
||||
"id": 137492,
|
||||
"status": 0,
|
||||
"series_signature": {
|
||||
"id": 3869898,
|
||||
"framework_id": 1,
|
||||
"signature_hash": "50fbde7bb038a62d5fe6e00ffa2ea23c31125b7c",
|
||||
"machine_platform": "macosx1015-64-shippable-qr",
|
||||
"suite": "perf_reftest_singletons",
|
||||
"test": "id-getter-2.html",
|
||||
"lower_is_better": true,
|
||||
"has_subtests": false,
|
||||
"option_collection_hash": "102210fe594ee9b33d82058545b1ed14f4c8206e",
|
||||
"tags": [""],
|
||||
"extra_options": ["e10s", "fission", "stylo", "webrender"],
|
||||
"measurement_unit": "ms",
|
||||
"suite_public_name": null,
|
||||
"test_public_name": null
|
||||
},
|
||||
"is_regression": true,
|
||||
"prev_value": 890.04,
|
||||
"new_value": 945.52,
|
||||
"t_value": 9.96,
|
||||
"amount_abs": 55.48,
|
||||
"amount_pct": 6.23,
|
||||
"summary_id": 32204,
|
||||
"related_summary_id": null,
|
||||
"manually_created": false,
|
||||
"classifier": null,
|
||||
"starred": false,
|
||||
"classifier_email": null,
|
||||
"backfill_record": {
|
||||
"context": "[{\"perf_datum_id\": 1438588964, \"value\": 858.7900000000001, \"job_id\": 356551789, \"push_id\": 980300, \"push_timestamp\": \"2021-10-27T22:38:38\", \"push__revision\": \"3d8df6da990da00dae776904d851c50c80aeb16e\"}, {\"perf_datum_id\": 1438591284, \"value\": 900.6400000000001, \"job_id\": 356552898, \"push_id\": 980306, \"push_timestamp\": \"2021-10-27T22:47:19\", \"push__revision\": \"909433910d0d03229dd23cc430d08d6326b44a95\"}, {\"perf_datum_id\": 1437405462, \"value\": 957.5899999999999, \"job_id\": 356285912, \"push_id\": 980323, \"push_timestamp\": \"2021-10-27T23:16:04\", \"push__revision\": \"3d8181bdd6c2b372d65125e9e68b7771d70fce8a\"}, {\"perf_datum_id\": 1437433742, \"value\": 932.48, \"job_id\": 356290338, \"push_id\": 980349, \"push_timestamp\": \"2021-10-28T00:09:34\", \"push__revision\": \"617a98c2fc8fb577df2c6c04e3c08f78dfc65c07\"}, {\"perf_datum_id\": 1438589209, \"value\": 877.1500000000001, \"job_id\": 356552355, \"push_id\": 980360, \"push_timestamp\": \"2021-10-28T00:39:38\", \"push__revision\": \"184d074f07d7d64f5aad4ccb7b683b2e937d3548\"}]",
|
||||
"status": 0,
|
||||
"total_backfills_triggered": 0
|
||||
},
|
||||
"noise_profile": "OK"
|
||||
}
|
||||
],
|
||||
"related_alerts": [],
|
||||
"status": 0,
|
||||
"bug_number": null,
|
||||
"bug_updated": null,
|
||||
"issue_tracker": 1,
|
||||
"notes": null,
|
||||
"revision": "462d2d4ba0516adc69de20372b16931cef20de9e",
|
||||
"push_timestamp": 1635376564,
|
||||
"prev_push_revision": "909433910d0d03229dd23cc430d08d6326b44a95",
|
||||
"assignee_username": null,
|
||||
"assignee_email": null,
|
||||
"performance_tags": []
|
||||
}
|
||||
]
|
||||
]
|
|
@ -20,6 +20,7 @@ import testData from '../../mock/performance_summary.json';
|
|||
import changelogData from '../../mock/infra_changelog.json';
|
||||
import seriesData from '../../mock/performance_signature_formatted.json';
|
||||
import seriesData2 from '../../mock/performance_signature_formatted2.json';
|
||||
import commonAlerts from '../../mock/alert_summaries_common.json';
|
||||
import { getProjectUrl } from '../../../../ui/helpers/location';
|
||||
import { createGraphData } from '../../../../ui/perfherder/perf-helpers/helpers';
|
||||
import {
|
||||
|
@ -35,6 +36,7 @@ const graphData = createGraphData(
|
|||
[],
|
||||
[...graphColors],
|
||||
[...graphSymbols],
|
||||
[...commonAlerts],
|
||||
);
|
||||
const inputPlaceholder = 'filter tests e.g. linux tp5o';
|
||||
const frameworks = [
|
||||
|
@ -386,3 +388,18 @@ test("'Highlight infra changes' button can be turned off", async () => {
|
|||
|
||||
expect(updateStateParams).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("'Highlight other alerts' button can be turned on", async () => {
|
||||
const updateStateParams = jest.fn();
|
||||
const { getByText } = graphsViewControls(graphData, false, updateStateParams);
|
||||
|
||||
const commonAlertsButton = await waitFor(() =>
|
||||
getByText('Highlight common alerts'),
|
||||
);
|
||||
|
||||
expect(commonAlertsButton.classList).not.toContain('active');
|
||||
|
||||
fireEvent.click(commonAlertsButton);
|
||||
|
||||
expect(updateStateParams).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
} from '../../../ui/perfherder/perf-helpers/constants';
|
||||
import repos from '../mock/repositories';
|
||||
import testData from '../mock/performance_summary.json';
|
||||
import commonAlerts from '../mock/alert_summaries_common.json';
|
||||
import { createGraphData } from '../../../ui/perfherder/perf-helpers/helpers';
|
||||
|
||||
const graphData = createGraphData(
|
||||
|
@ -15,6 +16,7 @@ const graphData = createGraphData(
|
|||
[],
|
||||
[...graphColors],
|
||||
[...graphSymbols],
|
||||
[...commonAlerts],
|
||||
);
|
||||
|
||||
const tableView = (
|
||||
|
|
|
@ -59,6 +59,7 @@ const GraphTooltip = ({
|
|||
const deltaPercent = value / v0 - 1;
|
||||
let alert;
|
||||
let alertStatus;
|
||||
let isCommonAlert = false;
|
||||
|
||||
if (dataPointDetails.alertSummary && dataPointDetails.alertSummary.alerts) {
|
||||
alert = dataPointDetails.alertSummary.alerts.find(
|
||||
|
@ -66,6 +67,10 @@ const GraphTooltip = ({
|
|||
);
|
||||
}
|
||||
|
||||
if (dataPointDetails.commonAlert) {
|
||||
isCommonAlert = true;
|
||||
}
|
||||
|
||||
if (alert) {
|
||||
alertStatus =
|
||||
alert.status === alertStatusMap.acknowledged && testDetails.alertSummary
|
||||
|
@ -199,6 +204,9 @@ const GraphTooltip = ({
|
|||
Could be affected by infra changes.
|
||||
</p>
|
||||
)}
|
||||
{isCommonAlert && !dataPointDetails.alertSummary && (
|
||||
<p className="small text-danger">Common alert</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
|
|
@ -45,6 +45,7 @@ class GraphsContainer extends React.Component {
|
|||
const zoomDomain = this.initZoomDomain(scatterPlotData);
|
||||
this.state = {
|
||||
highlights: [],
|
||||
highlightCommonAlertsData: [],
|
||||
scatterPlotData,
|
||||
zoomDomain,
|
||||
lockTooltip: false,
|
||||
|
@ -70,6 +71,7 @@ class GraphsContainer extends React.Component {
|
|||
componentDidUpdate(prevProps) {
|
||||
const {
|
||||
highlightAlerts,
|
||||
highlightCommonAlerts,
|
||||
highlightChangelogData,
|
||||
highlightedRevisions,
|
||||
testData,
|
||||
|
@ -81,6 +83,7 @@ class GraphsContainer extends React.Component {
|
|||
|
||||
if (
|
||||
prevProps.highlightAlerts !== highlightAlerts ||
|
||||
prevProps.highlightCommonAlerts !== highlightCommonAlerts ||
|
||||
prevProps.highlightChangelogData !== highlightChangelogData ||
|
||||
prevProps.highlightedRevisions !== highlightedRevisions
|
||||
) {
|
||||
|
@ -185,8 +188,14 @@ class GraphsContainer extends React.Component {
|
|||
};
|
||||
|
||||
addHighlights = () => {
|
||||
const { testData, highlightAlerts, highlightedRevisions } = this.props;
|
||||
const {
|
||||
testData,
|
||||
highlightAlerts,
|
||||
highlightCommonAlerts,
|
||||
highlightedRevisions,
|
||||
} = this.props;
|
||||
let highlights = [];
|
||||
let highlightCommonAlertsData = [];
|
||||
|
||||
for (const series of testData) {
|
||||
if (!series.visible) {
|
||||
|
@ -198,6 +207,16 @@ class GraphsContainer extends React.Component {
|
|||
highlights = [...highlights, ...dataPoints];
|
||||
}
|
||||
|
||||
if (highlightCommonAlerts) {
|
||||
const dataPoints = series.data.filter(
|
||||
(item) => item.commonAlert && !item.alertSummary,
|
||||
);
|
||||
highlightCommonAlertsData = [
|
||||
...highlightCommonAlertsData,
|
||||
...dataPoints,
|
||||
];
|
||||
}
|
||||
|
||||
for (const rev of highlightedRevisions) {
|
||||
if (!rev) {
|
||||
continue;
|
||||
|
@ -212,7 +231,7 @@ class GraphsContainer extends React.Component {
|
|||
}
|
||||
}
|
||||
}
|
||||
this.setState({ highlights });
|
||||
this.setState({ highlights, highlightCommonAlertsData });
|
||||
};
|
||||
|
||||
getTooltipPosition = (point, yOffset = 15) => ({
|
||||
|
@ -364,9 +383,11 @@ class GraphsContainer extends React.Component {
|
|||
zoom,
|
||||
highlightedRevisions,
|
||||
highlightChangelogData,
|
||||
highlightCommonAlerts,
|
||||
} = this.props;
|
||||
const {
|
||||
highlights,
|
||||
highlightCommonAlertsData,
|
||||
scatterPlotData,
|
||||
zoomDomain,
|
||||
lockTooltip,
|
||||
|
@ -556,6 +577,22 @@ class GraphsContainer extends React.Component {
|
|||
/>
|
||||
))}
|
||||
|
||||
{highlightCommonAlerts &&
|
||||
highlightCommonAlertsData.length > 0 &&
|
||||
highlightCommonAlertsData.map((item) => (
|
||||
<VictoryLine
|
||||
key={item}
|
||||
style={{
|
||||
data: {
|
||||
stroke: 'gray',
|
||||
strokeWidth: 1,
|
||||
strokeDasharray: '5',
|
||||
},
|
||||
}}
|
||||
x={() => item.x}
|
||||
/>
|
||||
))}
|
||||
|
||||
{highlightChangelogData && changelogData.length > 0 && (
|
||||
<VictoryBar
|
||||
key="changelog"
|
||||
|
|
|
@ -7,6 +7,8 @@ import queryString from 'query-string';
|
|||
import { getData, processResponse, processErrors } from '../../helpers/http';
|
||||
import {
|
||||
createApiUrl,
|
||||
createQueryParams,
|
||||
getApiUrl,
|
||||
parseQueryParams,
|
||||
updateQueryParams,
|
||||
} from '../../helpers/url';
|
||||
|
@ -37,6 +39,7 @@ class GraphsView extends React.Component {
|
|||
zoom: {},
|
||||
selectedDataPoint: null,
|
||||
highlightAlerts: true,
|
||||
highlightCommonAlerts: false,
|
||||
highlightChangelogData: true,
|
||||
highlightedRevisions: ['', ''],
|
||||
testData: [],
|
||||
|
@ -87,6 +90,7 @@ class GraphsView extends React.Component {
|
|||
zoom,
|
||||
selected,
|
||||
highlightAlerts,
|
||||
highlightCommonAlerts,
|
||||
highlightChangelogData,
|
||||
highlightedRevisions,
|
||||
} = queryString.parse(this.props.location.search);
|
||||
|
@ -103,6 +107,12 @@ class GraphsView extends React.Component {
|
|||
updates.highlightAlerts = Boolean(parseInt(highlightAlerts, 10));
|
||||
}
|
||||
|
||||
if (highlightCommonAlerts) {
|
||||
updates.highlightCommonAlerts = Boolean(
|
||||
parseInt(highlightCommonAlerts, 10),
|
||||
);
|
||||
}
|
||||
|
||||
if (highlightChangelogData) {
|
||||
updates.highlightChangelogData = Boolean(
|
||||
parseInt(highlightChangelogData, 10),
|
||||
|
@ -197,6 +207,9 @@ class GraphsView extends React.Component {
|
|||
this.getAlertSummaries(series.signature_id, series.repository_id),
|
||||
),
|
||||
);
|
||||
const commonAlerts = await Promise.all(
|
||||
seriesData.map((series) => this.getCommonAlerts(series.framework_id)),
|
||||
);
|
||||
const newColors = [...colors];
|
||||
const newSymbols = [...symbols];
|
||||
|
||||
|
@ -205,6 +218,7 @@ class GraphsView extends React.Component {
|
|||
alertSummaries.flat(),
|
||||
newColors,
|
||||
newSymbols,
|
||||
commonAlerts,
|
||||
);
|
||||
|
||||
this.setState({ colors: newColors, symbols: newSymbols });
|
||||
|
@ -229,6 +243,17 @@ class GraphsView extends React.Component {
|
|||
return [];
|
||||
};
|
||||
|
||||
getCommonAlerts = async (frameworkId) => {
|
||||
const params = { framework: frameworkId };
|
||||
const url = getApiUrl(
|
||||
`${endpoints.alertSummary}${createQueryParams(params)}`,
|
||||
);
|
||||
const response = await getData(url);
|
||||
const commonAlerts = [...response.data.results];
|
||||
|
||||
return commonAlerts;
|
||||
};
|
||||
|
||||
updateData = async (
|
||||
signatureId,
|
||||
repositoryName,
|
||||
|
@ -292,6 +317,7 @@ class GraphsView extends React.Component {
|
|||
selectedDataPoint,
|
||||
zoom,
|
||||
highlightAlerts,
|
||||
highlightCommonAlerts,
|
||||
highlightChangelogData,
|
||||
highlightedRevisions,
|
||||
timeRange,
|
||||
|
@ -304,6 +330,7 @@ class GraphsView extends React.Component {
|
|||
const params = {
|
||||
series: newSeries,
|
||||
highlightAlerts: +highlightAlerts,
|
||||
highlightCommonAlerts: +highlightCommonAlerts,
|
||||
highlightChangelogData: +highlightChangelogData,
|
||||
timerange: timeRange.value,
|
||||
zoom,
|
||||
|
@ -339,6 +366,7 @@ class GraphsView extends React.Component {
|
|||
timeRange,
|
||||
testData,
|
||||
highlightAlerts,
|
||||
highlightCommonAlerts,
|
||||
highlightChangelogData,
|
||||
highlightedRevisions,
|
||||
selectedDataPoint,
|
||||
|
@ -426,6 +454,7 @@ class GraphsView extends React.Component {
|
|||
highlightAlerts={highlightAlerts}
|
||||
highlightChangelogData={highlightChangelogData}
|
||||
highlightedRevisions={highlightedRevisions}
|
||||
highlightCommonAlerts={highlightCommonAlerts}
|
||||
zoom={zoom}
|
||||
selectedDataPoint={selectedDataPoint}
|
||||
updateStateParams={(state) =>
|
||||
|
|
|
@ -73,6 +73,7 @@ export default class GraphsViewControls extends React.Component {
|
|||
highlightAlerts,
|
||||
highlightChangelogData,
|
||||
highlightedRevisions,
|
||||
highlightCommonAlerts,
|
||||
updateTimeRange,
|
||||
hasNoData,
|
||||
toggle,
|
||||
|
@ -182,6 +183,19 @@ export default class GraphsViewControls extends React.Component {
|
|||
>
|
||||
Highlight infra changes
|
||||
</Button>
|
||||
<Button
|
||||
className="ml-3"
|
||||
color="darker-info"
|
||||
outline
|
||||
onClick={() =>
|
||||
updateStateParams({
|
||||
highlightCommonAlerts: !highlightCommonAlerts,
|
||||
})
|
||||
}
|
||||
active={highlightCommonAlerts}
|
||||
>
|
||||
Highlight common alerts
|
||||
</Button>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
|
|
|
@ -744,7 +744,13 @@ export const retriggerMultipleJobs = async (
|
|||
);
|
||||
};
|
||||
|
||||
export const createGraphData = (seriesData, alertSummaries, colors, symbols) =>
|
||||
export const createGraphData = (
|
||||
seriesData,
|
||||
alertSummaries,
|
||||
colors,
|
||||
symbols,
|
||||
commonAlerts,
|
||||
) =>
|
||||
seriesData.map((series) => {
|
||||
const color = colors.pop();
|
||||
const symbol = symbols.pop();
|
||||
|
@ -776,6 +782,9 @@ export const createGraphData = (seriesData, alertSummaries, colors, symbols) =>
|
|||
alertSummary: alertSummaries.find(
|
||||
(item) => item.push_id === dataPoint.push_id,
|
||||
),
|
||||
commonAlert: commonAlerts.some((item) => {
|
||||
return item.find((alert) => alert.push_id === dataPoint.push_id);
|
||||
}),
|
||||
signature_id: series.signature_id,
|
||||
pushId: dataPoint.push_id,
|
||||
jobId: dataPoint.job_id,
|
||||
|
|
Загрузка…
Ссылка в новой задаче