Bug 1723963 - Alerts View - Update Test column (#7231)

* Bug 1723963 - Eliminate duplicated name for suite and test

* Bug 1723963 - Add unit tests

* Bug 1723963 - Fix unit tests

* Bug 1723963 - Move TextualSummary class, fix duplication test name for bugzilla

* Bug 1723963 - Reset testAlert signature's values

* Bug 1723963 - Rename Suite & Test column to Test for bug description
This commit is contained in:
esanuandra 2021-08-06 17:05:32 +03:00 коммит произвёл GitHub
Родитель 44e244d753
Коммит 42cd50e794
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 258 добавлений и 204 удалений

Просмотреть файл

@ -24,7 +24,7 @@ const frameworks = [
},
];
const alertTableRowTest = (tags, alert = testAlert, options) => {
const alertTableRowTest = ({ alert, tags, options } = { alert: testAlert }) => {
if (tags) {
testAlert.series_signature.tags = [...tags];
}
@ -54,7 +54,7 @@ const alertTableRowTest = (tags, alert = testAlert, options) => {
afterEach(cleanup);
test('Test column contains only suite and test name', async () => {
const { getByTestId } = alertTableRowTest(false, testAlert);
const { getByTestId } = alertTableRowTest({ alert: testAlert, tags: false });
const { suite, test } = testAlert.series_signature;
const alertTitle = await waitFor(() =>
@ -64,8 +64,28 @@ test('Test column contains only suite and test name', async () => {
expect(alertTitle.textContent).toBe(`${suite} ${test}`);
});
test('Tests with duplicated suite and test name appears only once in Test column', async () => {
const { suite, test } = testAlert.series_signature;
testAlert.series_signature.suite = 'duplicatedName';
testAlert.series_signature.test = 'duplicatedName';
const { getByTestId } = alertTableRowTest({
alert: testAlert,
tags: false,
});
testAlert.series_signature.suite = suite;
testAlert.series_signature.test = test;
const alertTitle = await waitFor(() =>
getByTestId(`alert ${testAlert.id} title`),
);
expect(alertTitle.textContent).toBe('duplicatedName ');
});
test(`Platform column contains alerts's platform`, async () => {
const { getByTestId } = alertTableRowTest(false, testAlert);
const { getByTestId } = alertTableRowTest({ alert: testAlert, tags: false });
const { machine_platform: machinePlatform } = testAlert.series_signature;
const alertPlatform = await waitFor(() => getByTestId(`alert-platform`));
@ -74,7 +94,7 @@ test(`Platform column contains alerts's platform`, async () => {
});
test("Alert item with no tags displays 'No tags'", async () => {
const { getByText } = alertTableRowTest(['']);
const { getByText } = alertTableRowTest({ alert: testAlert, tags: [''] });
const message = await waitFor(() => getByText('No tags'));
expect(message).toBeInTheDocument();
@ -82,7 +102,10 @@ test("Alert item with no tags displays 'No tags'", async () => {
test('Alert item with 2 tags displays 2 tags', async () => {
const testTags = ['tag1', 'tag2'];
const { getAllByTestId } = alertTableRowTest(testTags);
const { getAllByTestId } = alertTableRowTest({
alert: testAlert,
tags: testTags,
});
const tags = await waitFor(() => getAllByTestId(`alert-tag`));
@ -91,7 +114,10 @@ test('Alert item with 2 tags displays 2 tags', async () => {
test("Alert item with more than 2 tags displays '...' button", async () => {
const testTags = ['tag1', 'tag2', 'tag3'];
const { getByTestId } = alertTableRowTest(testTags);
const { getByTestId } = alertTableRowTest({
alert: testAlert,
tags: testTags,
});
const showMoreButton = await waitFor(() => getByTestId('show-more-tags'));
@ -100,7 +126,10 @@ test("Alert item with more than 2 tags displays '...' button", async () => {
test("Button '...' displays all the tags for an alert item", async () => {
const testTags = ['tag1', 'tag2', 'tag3'];
const { getByTestId, getAllByTestId } = alertTableRowTest(testTags);
const { getByTestId, getAllByTestId } = alertTableRowTest({
alert: testAlert,
tags: testTags,
});
let visibleTags = await waitFor(() => getAllByTestId(`alert-tag`));
@ -118,7 +147,11 @@ test("Button '...' displays all the tags for an alert item", async () => {
});
test("Alert item with no options displays 'No options'", async () => {
const { getByText } = alertTableRowTest(false, testAlert, ['']);
const { getByText } = alertTableRowTest({
alert: testAlert,
tags: false,
options: [''],
});
const message = await waitFor(() => getByText('No options'));
expect(message).toBeInTheDocument();
@ -126,7 +159,11 @@ test("Alert item with no options displays 'No options'", async () => {
test('Alert item with 2 options displays 2 options', async () => {
const testOptions = ['option1', 'option2'];
const { getAllByTestId } = alertTableRowTest(false, testAlert, testOptions);
const { getAllByTestId } = alertTableRowTest({
alert: testAlert,
tags: false,
options: testOptions,
});
const options = await waitFor(() => getAllByTestId(`alert-option`));
@ -135,7 +172,11 @@ test('Alert item with 2 options displays 2 options', async () => {
test("Alert item with more than 2 options displays '...' button", async () => {
const testOptions = ['option1', 'option2', 'option3'];
const { getByTestId } = alertTableRowTest(false, testAlert, testOptions);
const { getByTestId } = alertTableRowTest({
alert: testAlert,
tags: false,
options: testOptions,
});
const showMoreButton = await waitFor(() => getByTestId('show-more-options'));
@ -144,11 +185,11 @@ test("Alert item with more than 2 options displays '...' button", async () => {
test("Button '...' displays all options for an alert item", async () => {
const testOptions = ['option1', 'option2', 'option3'];
const { getByTestId, getAllByTestId } = alertTableRowTest(
false,
testAlert,
testOptions,
);
const { getByTestId, getAllByTestId } = alertTableRowTest({
alert: testAlert,
tags: false,
options: testOptions,
});
let visibleOptions = await waitFor(() => getAllByTestId(`alert-option`));
@ -174,12 +215,18 @@ test('Documentation link is available for talos framework', async () => {
});
test('Documentation link is not available for build_metrics framework', async () => {
const { queryByTestId } = alertTableRowTest(false, testAlert2);
const { queryByTestId } = alertTableRowTest({
alert: testAlert2,
tags: false,
});
expect(queryByTestId('docs')).toBeNull();
});
test('Chart icon opens the graph link for an alert in a new tab', async () => {
const { getByLabelText } = alertTableRowTest(false, testAlert);
const { getByLabelText } = alertTableRowTest({
alert: testAlert,
tags: false,
});
const graphLink = await waitFor(() => getByLabelText('graph-link'));

Просмотреть файл

@ -145,11 +145,11 @@ export default class AlertTableRow extends React.Component {
(
{statusColor === 'text-success' && (
<FontAwesomeIcon icon={faCheck} color="#28a745" />
)}{' '}
)}
<span className={statusColor}>{alertStatus}</span>
{alert.related_summary_id && this.getReassignment(alert)}
{alert.backfill_record ? (
<span className="text-darker-info">, important </span>
<span className="text-darker-info">, important</span>
) : null}
)
</React.Fragment>
@ -181,9 +181,10 @@ export default class AlertTableRow extends React.Component {
const { title } = alert;
const { suite, test } = alert.series_signature;
const { url } = getSplitTestTitle(title, suite, frameworkName);
const duplicatedName = suite === test;
return (
<span>
<span
<div>
<div
className={textEffect}
id={`alert ${alert.id} title`}
title={alert.backfill_record ? backfillRetriggeredTitle : ''}
@ -196,27 +197,29 @@ export default class AlertTableRow extends React.Component {
<a data-testid="docs" href={url}>
{suite}
</a>{' '}
{test}
{!duplicatedName && test}
</span>
) : (
<span data-testid={`alert ${alert.id} title`}>
{suite} {test}
{suite} {!duplicatedName && test}
</span>
)}
</span>{' '}
{this.renderAlertStatus(alert, alertStatus, statusColor)}{' '}
<span className="result-links">
{alert.series_signature.has_subtests && (
<a
href={this.getSubtestsURL()}
target="_blank"
rel="noopener noreferrer"
>
· subtests
</a>
)}{' '}
</span>
</span>
</div>
<div>
{this.renderAlertStatus(alert, alertStatus, statusColor)}{' '}
<span className="result-links">
{alert.series_signature.has_subtests && (
<a
href={this.getSubtestsURL()}
target="_blank"
rel="noopener noreferrer"
>
· subtests
</a>
)}
</span>
</div>
</div>
);
};

Просмотреть файл

@ -13,12 +13,12 @@ import templateSettings from 'lodash/templateSettings';
import {
getFrameworkName,
TextualSummary,
getFilledBugSummary,
getStatus,
updateAlertSummary,
} from '../perf-helpers/helpers';
import { getData } from '../../helpers/http';
import TextualSummary from '../perf-helpers/textualSummary';
import {
getApiUrl,
bzBaseUrl,

Просмотреть файл

@ -1,6 +1,5 @@
import moment from 'moment';
import numeral from 'numeral';
import sortBy from 'lodash/sortBy';
import queryString from 'query-string';
import { getApiUrl } from '../../helpers/url';
@ -23,7 +22,6 @@ import {
phFrameworksWithRelatedBranches,
phTimeRanges,
unknownFrameworkMessage,
testDocumentationFrameworks,
} from './constants';
export const formatNumber = (input) =>
@ -454,170 +452,6 @@ export const getSplitTestTitle = (title, suite, framework) => {
return { url, remainingTestName };
};
export class TextualSummary {
constructor(
frameworks,
alerts,
alertSummary,
copySummary = null,
alertsWithVideos = [],
) {
this.frameworks = frameworks;
this.alerts = alerts;
this.alertSummary = alertSummary;
this.copySummary = copySummary;
this.alertsWithVideos = alertsWithVideos;
this.ellipsesRow = `\n|...|...|...|...|...|...|`;
}
get markdown() {
let resultStr = '';
const improved = sortBy(
this.alerts.filter((alert) => !alert.is_regression),
'amount_pct',
).reverse();
const regressed = sortBy(
this.alerts.filter(
(alert) =>
alert.is_regression && alert.status !== alertStatusMap.invalid,
),
'amount_pct',
).reverse();
const regressionsTable = this.getFormattedRegressions(regressed);
resultStr += regressionsTable;
const improvementsTable = this.getFormattedImprovements(improved);
resultStr += improvementsTable;
if (this.copySummary) {
resultStr = this.attachSummaryHeaderAndAlertLink(resultStr);
}
return resultStr;
}
attachSummaryHeaderAndAlertLink = (resultStr) => {
// add summary header if getting text for clipboard only
const created = new Date(this.alertSummary.created);
const summaryHeader = `== Change summary for alert #${
this.alertSummary.id
} (as of ${created.toUTCString()}) ==\n`;
resultStr = summaryHeader + resultStr;
// include link to alert if getting text for clipboard only
const alertLink = `${window.location.origin}/perfherder/alerts?id=${this.alertSummary.id}`;
resultStr += `\nFor up to date results, see: ${alertLink}`;
return resultStr;
};
formatAlert(alert) {
const numFormat = '0,0.00';
let amountPct;
if (alert.amount_pct.toFixed(0) === '0') {
// have extra fraction digits when rounding ends up with 0%
amountPct = alert.amount_pct.toFixed(2);
} else {
amountPct = alert.amount_pct.toFixed(0);
}
const prevValue = numeral(alert.prev_value).format(numFormat);
const newValue = numeral(alert.new_value).format(numFormat);
const { suite, test, machine_platform: platform } = alert.series_signature;
const extraOptions = alert.series_signature.extra_options.join(' ');
const updatedAlert = this.alertsWithVideos.find((a) => alert.id === a.id);
const frameworkName = getFrameworkName(
this.frameworks,
this.alertSummary.framework,
);
const url = getTestDocumentationURL(frameworkName, suite);
const suiteName = testDocumentationFrameworks.includes(frameworkName)
? `[${suite}](${url})`
: suite;
if (
updatedAlert &&
updatedAlert.results_link &&
updatedAlert.prev_results_link
) {
return `| ${amountPct}% | ${suiteName} | ${test} | ${platform} | ${extraOptions} | [${prevValue}](${updatedAlert.prev_results_link}) -> [${newValue}](${updatedAlert.results_link}) |`;
}
return `| ${amountPct}% | ${suiteName} | ${test} | ${platform} | ${extraOptions} | ${prevValue} -> ${newValue} |`;
}
formatAlertBulk(alerts) {
return alerts.map((alert) => this.formatAlert(alert, alerts)).join('\n');
}
getFormattedRegressions(regressed) {
let resultStr = '';
if (regressed.length > 0 && regressed.length <= 15) {
// add a newline if we displayed the header
if (this.copySummary) {
resultStr += '\n';
}
const formattedRegressions = this.formatAlertBulk(regressed);
resultStr += `### Regressions:\n\n| **Ratio** | **Suite** | **Test** | **Platform** | **Options** | **Absolute values (old vs new)**| \n|--|--|--|--|--|--| \n${formattedRegressions}\n`;
}
if (regressed.length > 15) {
// add a newline if we displayed the header
if (this.copySummary) {
resultStr += '\n';
}
const sortedRegressed = regressed.sort(
(a, b) => b.amount_pct - a.amount_pct,
);
const biggestTenRegressed = sortedRegressed.slice(0, 10);
const smallestFiveRegressed = sortedRegressed.slice(-5);
const formattedBiggestRegressions = this.formatAlertBulk(
biggestTenRegressed,
);
const formattedSmallestRegressions = this.formatAlertBulk(
smallestFiveRegressed,
);
resultStr += `### Regressions:\n\n| **Ratio** | **Suite** | **Test** | **Platform** | **Options** | **Absolute values (old vs new)**| \n|--|--|--|--|--|--| \n${formattedBiggestRegressions}`;
resultStr += this.ellipsesRow;
resultStr += `\n${formattedSmallestRegressions}\n`;
}
return resultStr;
}
getFormattedImprovements(improved) {
let resultStr = '';
if (improved.length > 0 && improved.length <= 6) {
// Add a newline if we displayed some regressions
if (resultStr.length > 0) {
resultStr += '\n';
}
const formattedImprovements = this.formatAlertBulk(improved);
resultStr += `### Improvements:\n\n| **Ratio** | **Suite** | **Test** | **Platform** | **Options** | **Absolute values (old vs new)**| \n|--|--|--|--|--|--| \n${formattedImprovements}\n`;
} else if (improved.length > 6) {
// Add a newline if we displayed some regressions
if (resultStr.length > 0) {
resultStr += '\n';
}
const sortedImproved = improved.sort(
(a, b) => b.amount_pct - a.amount_pct,
);
const biggestFiveImprovements = sortedImproved.slice(0, 5);
const smallestImprovement = sortedImproved.slice(-1);
const formattedBiggestImprovements = this.formatAlertBulk(
biggestFiveImprovements,
);
const formattedSmallestImprovement = this.formatAlertBulk(
smallestImprovement,
);
resultStr += `### Improvements:\n\n| **Ratio** | **Suite** | **Test** | **Platform** | **Options** | **Absolute values (old vs new)**| \n|--|--|--|--|--|--| \n${formattedBiggestImprovements}`;
resultStr += this.ellipsesRow;
resultStr += `\n${formattedSmallestImprovement}\n`;
}
return resultStr;
}
}
const getPlatformInfo = (platforms) => {
const platformInfo = [];
platforms.forEach((platform) =>

Просмотреть файл

@ -0,0 +1,170 @@
import numeral from 'numeral';
import sortBy from 'lodash/sortBy';
import { alertStatusMap, testDocumentationFrameworks } from './constants';
import { getFrameworkName, getTestDocumentationURL } from './helpers';
export default class TextualSummary {
constructor(
frameworks,
alerts,
alertSummary,
copySummary = null,
alertsWithVideos = [],
) {
this.frameworks = frameworks;
this.alerts = alerts;
this.alertSummary = alertSummary;
this.copySummary = copySummary;
this.alertsWithVideos = alertsWithVideos;
this.ellipsesRow = `\n|...|...|...|...|...|`;
}
get markdown() {
let resultStr = '';
const improved = sortBy(
this.alerts.filter((alert) => !alert.is_regression),
'amount_pct',
).reverse();
const regressed = sortBy(
this.alerts.filter(
(alert) =>
alert.is_regression && alert.status !== alertStatusMap.invalid,
),
'amount_pct',
).reverse();
const regressionsTable = this.getFormattedRegressions(regressed);
resultStr += regressionsTable;
const improvementsTable = this.getFormattedImprovements(improved);
resultStr += improvementsTable;
if (this.copySummary) {
resultStr = this.attachSummaryHeaderAndAlertLink(resultStr);
}
return resultStr;
}
attachSummaryHeaderAndAlertLink = (resultStr) => {
// add summary header if getting text for clipboard only
const created = new Date(this.alertSummary.created);
const summaryHeader = `== Change summary for alert #${
this.alertSummary.id
} (as of ${created.toUTCString()}) ==\n`;
resultStr = summaryHeader + resultStr;
// include link to alert if getting text for clipboard only
const alertLink = `${window.location.origin}/perfherder/alerts?id=${this.alertSummary.id}`;
resultStr += `\nFor up to date results, see: ${alertLink}`;
return resultStr;
};
formatAlert(alert) {
const numFormat = '0,0.00';
let amountPct;
if (alert.amount_pct.toFixed(0) === '0') {
// have extra fraction digits when rounding ends up with 0%
amountPct = alert.amount_pct.toFixed(2);
} else {
amountPct = alert.amount_pct.toFixed(0);
}
const prevValue = numeral(alert.prev_value).format(numFormat);
const newValue = numeral(alert.new_value).format(numFormat);
const { suite, test, machine_platform: platform } = alert.series_signature;
const extraOptions = alert.series_signature.extra_options.join(' ');
const updatedAlert = this.alertsWithVideos.find((a) => alert.id === a.id);
const frameworkName = getFrameworkName(
this.frameworks,
this.alertSummary.framework,
);
const url = getTestDocumentationURL(frameworkName, suite);
const suiteName = testDocumentationFrameworks.includes(frameworkName)
? `[${suite}](${url})`
: suite;
const suiteTestName = suite === test ? suiteName : `${suiteName} ${test}`;
if (
updatedAlert &&
updatedAlert.results_link &&
updatedAlert.prev_results_link
) {
return `| ${amountPct}% | ${suiteTestName} | ${platform} | ${extraOptions} | [${prevValue}](${updatedAlert.prev_results_link}) -> [${newValue}](${updatedAlert.results_link}) |`;
}
return `| ${amountPct}% | ${suiteTestName} | ${platform} | ${extraOptions} | ${prevValue} -> ${newValue} |`;
}
formatAlertBulk(alerts) {
return alerts.map((alert) => this.formatAlert(alert, alerts)).join('\n');
}
getFormattedRegressions(regressed) {
let resultStr = '';
if (regressed.length > 0 && regressed.length <= 15) {
// add a newline if we displayed the header
if (this.copySummary) {
resultStr += '\n';
}
const formattedRegressions = this.formatAlertBulk(regressed);
resultStr += `### Regressions:\n\n| **Ratio** | **Test** | **Platform** | **Options** | **Absolute values (old vs new)**| \n|--|--|--|--|--| \n${formattedRegressions}\n`;
}
if (regressed.length > 15) {
// add a newline if we displayed the header
if (this.copySummary) {
resultStr += '\n';
}
const sortedRegressed = regressed.sort(
(a, b) => b.amount_pct - a.amount_pct,
);
const biggestTenRegressed = sortedRegressed.slice(0, 10);
const smallestFiveRegressed = sortedRegressed.slice(-5);
const formattedBiggestRegressions = this.formatAlertBulk(
biggestTenRegressed,
);
const formattedSmallestRegressions = this.formatAlertBulk(
smallestFiveRegressed,
);
resultStr += `### Regressions:\n\n| **Ratio** | **Test** | **Platform** | **Options** | **Absolute values (old vs new)**| \n|--|--|--|--|--| \n${formattedBiggestRegressions}`;
resultStr += this.ellipsesRow;
resultStr += `\n${formattedSmallestRegressions}\n`;
}
return resultStr;
}
getFormattedImprovements(improved) {
let resultStr = '';
if (improved.length > 0 && improved.length <= 6) {
// Add a newline if we displayed some regressions
if (resultStr.length > 0) {
resultStr += '\n';
}
const formattedImprovements = this.formatAlertBulk(improved);
resultStr += `### Improvements:\n\n| **Ratio** | **Test** | **Platform** | **Options** | **Absolute values (old vs new)**| \n|--|--|--|--|--| \n${formattedImprovements}\n`;
} else if (improved.length > 6) {
// Add a newline if we displayed some regressions
if (resultStr.length > 0) {
resultStr += '\n';
}
const sortedImproved = improved.sort(
(a, b) => b.amount_pct - a.amount_pct,
);
const biggestFiveImprovements = sortedImproved.slice(0, 5);
const smallestImprovement = sortedImproved.slice(-1);
const formattedBiggestImprovements = this.formatAlertBulk(
biggestFiveImprovements,
);
const formattedSmallestImprovement = this.formatAlertBulk(
smallestImprovement,
);
resultStr += `### Improvements:\n\n| **Ratio** | **Test** | **Platform** | **Options** | **Absolute values (old vs new)**| \n|--|--|--|--|--| \n${formattedBiggestImprovements}`;
resultStr += this.ellipsesRow;
resultStr += `\n${formattedSmallestImprovement}\n`;
}
return resultStr;
}
}