* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { FlagMessage } from 'design-system';
import { debounce, findLast, maxBy, minBy, sortBy } from 'lodash';
import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
import GraphsHeader from '../../../components/activity-graph/GraphsHeader';
import GraphsHistory from '../../../components/activity-graph/GraphsHistory';
import GraphsZoom from '../../../components/activity-graph/GraphsZoom';
saveActivityGraph,
splitSeriesInGraphs,
} from '../../../components/activity-graph/utils';
+import DocumentationLink from '../../../components/common/DocumentationLink';
+import { CCT_SOFTWARE_QUALITY_METRICS } from '../../../helpers/constants';
+import { translate } from '../../../helpers/l10n';
import {
GraphType,
MeasureHistory,
}
};
+ renderQualitiesMetricInfoMessage = () => {
+ const { measuresHistory } = this.props;
+
+ const qualityMeasuresHistory = measuresHistory.find((history) =>
+ CCT_SOFTWARE_QUALITY_METRICS.includes(history.metric),
+ );
+
+ const indexOfFirstMeasureWithValue = qualityMeasuresHistory?.history.findIndex(
+ (item) => item.value,
+ );
+
+ const hasGaps =
+ indexOfFirstMeasureWithValue === -1
+ ? false
+ : qualityMeasuresHistory?.history
+ .slice(indexOfFirstMeasureWithValue)
+ .some((item) => item.value === undefined);
+
+ if (hasGaps) {
+ return (
+ <FlagMessage variant="info">
+ <FormattedMessage
+ id="project_activity.graphs.data_table.data_gap"
+ tagName="div"
+ values={{
+ learn_more: (
+ <DocumentationLink
+ className="sw-whitespace-nowrap"
+ to="/user-guide/clean-code/code-analysis/"
+ >
+ {translate('learn_more')}
+ </DocumentationLink>
+ ),
+ }}
+ />
+ </FlagMessage>
+ );
+ }
+
+ return null;
+ };
+
render() {
const { analyses, leakPeriodDate, loading, measuresHistory, metrics, query } = this.props;
const { graphEndDate, graphStartDate, series } = this.state;
selectedMetrics={query.customMetrics}
onUpdateGraph={this.handleUpdateGraph}
/>
+ {this.renderQualitiesMetricInfoMessage()}
<GraphsHistory
analyses={analyses}
graph={query.graph}
MetricKey.sqale_rating,
MetricKey.security_hotspots_reviewed,
MetricKey.security_review_rating,
+ MetricKey.maintainability_issues,
].map((metric) =>
mockMeasureHistory({
metric,
- history: projectActivityHandler
- .getAnalysesList()
- .map(({ date }) => mockHistoryItem({ value: '3', date: parseDate(date) })),
+ history: projectActivityHandler.getAnalysesList().map(({ date }) =>
+ mockHistoryItem({
+ value: '3',
+ date: parseDate(date),
+ }),
+ ),
}),
),
);
).not.toBeInTheDocument();
},
);
+
+ it('should render graph gap info message', async () => {
+ timeMachineHandler.setMeasureHistory([
+ mockMeasureHistory({
+ metric: MetricKey.maintainability_issues,
+ history: projectActivityHandler.getAnalysesList().map(({ date }, index) =>
+ mockHistoryItem({
+ // eslint-disable-next-line jest/no-conditional-in-test
+ value: index === 0 ? '3' : undefined,
+ date: parseDate(date),
+ }),
+ ),
+ }),
+ ]);
+ const { ui } = getPageObject();
+ renderProjectActivityAppContainer(
+ mockComponent({
+ breadcrumbs: [
+ { key: 'breadcrumb', name: 'breadcrumb', qualifier: ComponentQualifier.Application },
+ ],
+ }),
+ );
+
+ await ui.changeGraphType(GraphType.custom);
+ await ui.openMetricsDropdown();
+ await ui.toggleMetric(MetricKey.maintainability_issues);
+ expect(ui.gapInfoMessage.get()).toBeInTheDocument();
+ });
+
+ it('should not render graph gap info message if no gaps', async () => {
+ const { ui } = getPageObject();
+ renderProjectActivityAppContainer(
+ mockComponent({
+ breadcrumbs: [
+ { key: 'breadcrumb', name: 'breadcrumb', qualifier: ComponentQualifier.Application },
+ ],
+ }),
+ );
+
+ await ui.changeGraphType(GraphType.custom);
+ await ui.openMetricsDropdown();
+ await ui.toggleMetric(MetricKey.maintainability_issues);
+ expect(ui.gapInfoMessage.query()).not.toBeInTheDocument();
+ });
});
describe('CRUD', () => {
// Graphs.
graphs: byLabelText('project_activity.graphs.explanation_x', { exact: false }),
noDataText: byText('project_activity.graphs.custom.no_history'),
+ gapInfoMessage: byText('project_activity.graphs.data_table.data_gap', { exact: false }),
// Add metrics.
addMetricBtn: byRole('button', { name: 'project_activity.graphs.custom.add' }),
{
metrics: keyBy(
[
+ mockMetric({ key: MetricKey.maintainability_issues, type: MetricType.Data }),
mockMetric({ key: MetricKey.bugs, type: MetricType.Integer }),
mockMetric({ key: MetricKey.code_smells, type: MetricType.Integer }),
mockMetric({ key: MetricKey.security_hotspots_reviewed }),