aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Davis <jeremy.davis@sonarsource.com>2019-08-30 17:31:15 +0200
committerSonarTech <sonartech@sonarsource.com>2019-09-24 20:21:17 +0200
commit3079907d1f13a509709c72a5dade87d129a4eb5c (patch)
treed3a79825cbf8ae25a3f473f5cfc07ffbfd36eba2
parent45f8d835abdaf01287e2cf4a149b87ab2abfd156 (diff)
downloadsonarqube-3079907d1f13a509709c72a5dade87d129a4eb5c.tar.gz
sonarqube-3079907d1f13a509709c72a5dade87d129a4eb5c.zip
SONAR-12430 Handle project baseline for Community Edition
-rw-r--r--server/sonar-web/src/main/js/api/newCodePeriod.ts2
-rw-r--r--server/sonar-web/src/main/js/app/types.d.ts12
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/GraphsHistory.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.tsx7
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/utils.ts6
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/__tests__/App-test.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/__tests__/BranchBaselineSettingModal-test.tsx20
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/__tests__/ProjectBaselineSelector-test.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/BranchBaselineSettingModal-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/ProjectBaselineSelector-test.tsx.snap99
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/__tests__/utils-test.ts40
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx81
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/components/AppContainer.ts1
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisList.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/components/BranchBaselineSettingModal.tsx42
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineSelector.tsx88
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/styles.css16
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/utils.ts41
-rw-r--r--server/sonar-web/src/main/js/helpers/testMocks.ts3
25 files changed, 354 insertions, 161 deletions
diff --git a/server/sonar-web/src/main/js/api/newCodePeriod.ts b/server/sonar-web/src/main/js/api/newCodePeriod.ts
index 1c21cefa735..da2b9d83ea7 100644
--- a/server/sonar-web/src/main/js/api/newCodePeriod.ts
+++ b/server/sonar-web/src/main/js/api/newCodePeriod.ts
@@ -23,7 +23,7 @@ import throwGlobalError from '../app/utils/throwGlobalError';
export function getNewCodePeriod(data?: {
project?: string;
branch?: string;
-}): Promise<{ type: T.NewCodePeriodSettingType; inherited?: boolean; value?: string }> {
+}): Promise<T.Omit<T.NewCodePeriod, 'effectiveValue'>> {
return getJSON('/api/new_code_periods/show', data).catch(throwGlobalError);
}
diff --git a/server/sonar-web/src/main/js/app/types.d.ts b/server/sonar-web/src/main/js/app/types.d.ts
index d2a89083b22..703d7b8db5b 100644
--- a/server/sonar-web/src/main/js/app/types.d.ts
+++ b/server/sonar-web/src/main/js/app/types.d.ts
@@ -52,15 +52,22 @@ declare namespace T {
name: string;
}
- export interface Analysis {
+ interface BaseAnalysis {
buildString?: string;
- date: string;
events: AnalysisEvent[];
key: string;
manualNewCodePeriodBaseline?: boolean;
projectVersion?: string;
}
+ export interface Analysis extends BaseAnalysis {
+ date: string;
+ }
+
+ export interface ParsedAnalysis extends BaseAnalysis {
+ date: Date;
+ }
+
export interface AnalysisEvent {
category: string;
description?: string;
@@ -507,6 +514,7 @@ declare namespace T {
type?: NewCodePeriodSettingType;
value?: string;
effectiveValue?: string;
+ inherited?: boolean;
}
export interface NewCodePeriodBranch {
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/GraphsHistory.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/GraphsHistory.tsx
index c52f9217dce..468b80897af 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/GraphsHistory.tsx
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/GraphsHistory.tsx
@@ -26,13 +26,12 @@ import {
hasHistoryData,
isCustomGraph,
MeasureHistory,
- ParsedAnalysis,
Serie
} from '../utils';
import GraphHistory from './GraphHistory';
interface Props {
- analyses: ParsedAnalysis[];
+ analyses: T.ParsedAnalysis[];
eventFilter: string;
graph: string;
graphs: Serie[][];
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.tsx
index d6de9e60612..25171679c5c 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.tsx
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.tsx
@@ -28,7 +28,6 @@ import DateFormatter from '../../../components/intl/DateFormatter';
import {
activityQueryChanged,
getAnalysesByVersionByDay,
- ParsedAnalysis,
Query,
selectedDateQueryChanged
} from '../utils';
@@ -37,7 +36,7 @@ import ProjectActivityAnalysis from './ProjectActivityAnalysis';
interface Props {
addCustomEvent: (analysis: string, name: string, category?: string) => Promise<void>;
addVersion: (analysis: string, version: string) => Promise<void>;
- analyses: ParsedAnalysis[];
+ analyses: T.ParsedAnalysis[];
analysesLoading: boolean;
canAdmin?: boolean;
canDeleteAnalyses?: boolean;
@@ -157,14 +156,14 @@ export default class ProjectActivityAnalysesList extends React.PureComponent<Pro
this.props.updateQuery({ selectedDate: date });
};
- shouldRenderBaselineMarker(analysis: ParsedAnalysis): boolean {
+ shouldRenderBaselineMarker(analysis: T.ParsedAnalysis): boolean {
return Boolean(
analysis.manualNewCodePeriodBaseline ||
(this.props.leakPeriodDate && isEqual(this.props.leakPeriodDate, analysis.date))
);
}
- renderAnalysis(analysis: ParsedAnalysis) {
+ renderAnalysis(analysis: T.ParsedAnalysis) {
const firstAnalysisKey = this.props.analyses[0].key;
const selectedDate = this.props.query.selectedDate
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.tsx
index 515388f74d8..cc748a3041c 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.tsx
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.tsx
@@ -28,7 +28,6 @@ import Tooltip from 'sonar-ui-common/components/controls/Tooltip';
import { parseDate } from 'sonar-ui-common/helpers/dates';
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
import TimeFormatter from '../../../components/intl/TimeFormatter';
-import { ParsedAnalysis } from '../utils';
import Events from './Events';
import AddEventForm from './forms/AddEventForm';
import RemoveAnalysisForm from './forms/RemoveAnalysisForm';
@@ -36,7 +35,7 @@ import RemoveAnalysisForm from './forms/RemoveAnalysisForm';
interface Props {
addCustomEvent: (analysis: string, name: string, category?: string) => Promise<void>;
addVersion: (analysis: string, version: string) => Promise<void>;
- analysis: ParsedAnalysis;
+ analysis: T.ParsedAnalysis;
canAdmin?: boolean;
canDeleteAnalyses?: boolean;
canCreateVersion: boolean;
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx
index 8766d537f91..fef42d2314d 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx
@@ -23,7 +23,7 @@ import { parseDate } from 'sonar-ui-common/helpers/dates';
import { translate } from 'sonar-ui-common/helpers/l10n';
import A11ySkipTarget from '../../../app/components/a11y/A11ySkipTarget';
import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
-import { MeasureHistory, ParsedAnalysis, Query } from '../utils';
+import { MeasureHistory, Query } from '../utils';
import './projectActivity.css';
import ProjectActivityAnalysesList from './ProjectActivityAnalysesList';
import ProjectActivityGraphs from './ProjectActivityGraphs';
@@ -32,7 +32,7 @@ import ProjectActivityPageHeader from './ProjectActivityPageHeader';
interface Props {
addCustomEvent: (analysis: string, name: string, category?: string) => Promise<void>;
addVersion: (analysis: string, version: string) => Promise<void>;
- analyses: ParsedAnalysis[];
+ analyses: T.ParsedAnalysis[];
analysesLoading: boolean;
changeEvent: (event: string, name: string) => Promise<void>;
deleteAnalysis: (analysis: string) => Promise<void>;
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.tsx
index 7283ba97355..ce0e6a6196a 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.tsx
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.tsx
@@ -33,7 +33,6 @@ import {
getProjectActivityGraph,
isCustomGraph,
MeasureHistory,
- ParsedAnalysis,
parseQuery,
Query,
serializeQuery,
@@ -49,7 +48,7 @@ interface Props {
}
export interface State {
- analyses: ParsedAnalysis[];
+ analyses: T.ParsedAnalysis[];
analysesLoading: boolean;
graphLoading: boolean;
initialized: boolean;
@@ -158,7 +157,7 @@ export default class ProjectActivityAppContainer extends React.PureComponent<Pro
analyses: analyses.map(analysis => ({
...analysis,
date: parseDate(analysis.date)
- })) as ParsedAnalysis[],
+ })) as T.ParsedAnalysis[],
paging
}));
};
@@ -204,8 +203,8 @@ export default class ProjectActivityAppContainer extends React.PureComponent<Pro
loadAllActivities = (
project: string,
- prevResult?: { analyses: ParsedAnalysis[]; paging: T.Paging }
- ): Promise<{ analyses: ParsedAnalysis[]; paging: T.Paging }> => {
+ prevResult?: { analyses: T.ParsedAnalysis[]; paging: T.Paging }
+ ): Promise<{ analyses: T.ParsedAnalysis[]; paging: T.Paging }> => {
if (
prevResult &&
prevResult.paging.pageIndex * prevResult.paging.pageSize >= prevResult.paging.total
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx
index 2f7ac01cbda..d2c510a4a51 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx
@@ -29,7 +29,6 @@ import {
historyQueryChanged,
isCustomGraph,
MeasureHistory,
- ParsedAnalysis,
Point,
PROJECT_ACTIVITY_GRAPH,
PROJECT_ACTIVITY_GRAPH_CUSTOM,
@@ -42,7 +41,7 @@ import GraphsZoom from './GraphsZoom';
import ProjectActivityGraphsHeader from './ProjectActivityGraphsHeader';
interface Props {
- analyses: ParsedAnalysis[];
+ analyses: T.ParsedAnalysis[];
leakPeriodDate?: Date;
loading: boolean;
measuresHistory: MeasureHistory[];
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.tsx
index 3b0c9ff0e54..679cce88b1e 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.tsx
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.tsx
@@ -20,12 +20,11 @@
import * as React from 'react';
import ConfirmModal from 'sonar-ui-common/components/controls/ConfirmModal';
import { translate } from 'sonar-ui-common/helpers/l10n';
-import { ParsedAnalysis } from '../../utils';
interface Props {
addEvent: (analysis: string, name: string, category?: string) => Promise<void>;
addEventButtonText: string;
- analysis: ParsedAnalysis;
+ analysis: T.ParsedAnalysis;
onClose: () => void;
}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.tsx
index c3dee2cac2d..36fdc8d723d 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.tsx
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.tsx
@@ -20,10 +20,9 @@
import * as React from 'react';
import ConfirmModal from 'sonar-ui-common/components/controls/ConfirmModal';
import { translate } from 'sonar-ui-common/helpers/l10n';
-import { ParsedAnalysis } from '../../utils';
interface Props {
- analysis: ParsedAnalysis;
+ analysis: T.ParsedAnalysis;
deleteAnalysis: (analysis: string) => Promise<void>;
onClose: () => void;
}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/utils.ts b/server/sonar-web/src/main/js/apps/projectActivity/utils.ts
index 7d8c774edd2..82dd6bdd1bc 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/utils.ts
+++ b/server/sonar-web/src/main/js/apps/projectActivity/utils.ts
@@ -32,8 +32,6 @@ import {
} from 'sonar-ui-common/helpers/query';
import { get } from 'sonar-ui-common/helpers/storage';
-export type ParsedAnalysis = T.Omit<T.Analysis, 'date'> & { date: Date };
-
export interface Query {
category: string;
customMetrics: string[];
@@ -193,12 +191,12 @@ export function getSeriesMetricType(series: Serie[]) {
}
interface AnalysesByDay {
- byDay: T.Dict<ParsedAnalysis[]>;
+ byDay: T.Dict<T.ParsedAnalysis[]>;
version: string | null;
key: string | null;
}
-export function getAnalysesByVersionByDay(analyses: ParsedAnalysis[], query: Query) {
+export function getAnalysesByVersionByDay(analyses: T.ParsedAnalysis[], query: Query) {
return analyses.reduce<AnalysesByDay[]>((acc, analysis) => {
let currentVersion = acc[acc.length - 1];
const versionEvent = analysis.events.find(event => event.category === 'VERSION');
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/App-test.tsx
index 32b7edf81da..84c2a008476 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/App-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/App-test.tsx
@@ -93,6 +93,12 @@ it('should handle errors gracefully', async () => {
function shallowRender(props: Partial<App['props']> = {}) {
return shallow<App>(
- <App branchLikes={[]} canAdmin={true} component={mockComponent()} {...props} />
+ <App
+ branchLikes={[]}
+ branchesEnabled={true}
+ canAdmin={true}
+ component={mockComponent()}
+ {...props}
+ />
);
}
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/BranchBaselineSettingModal-test.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/BranchBaselineSettingModal-test.tsx
index 230a2ec74c9..abe14e18235 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/BranchBaselineSettingModal-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/BranchBaselineSettingModal-test.tsx
@@ -88,26 +88,6 @@ it('should disable the save button when date is invalid', () => {
).toBe(true);
});
-describe('getSettingValue', () => {
- const wrapper = shallowRender();
- wrapper.setState({ analysis: 'analysis1', days: '35' });
-
- it('should work for Days', () => {
- wrapper.setState({ selected: 'NUMBER_OF_DAYS' });
- expect(wrapper.instance().getSettingValue()).toBe('35');
- });
-
- it('should work for Analysis', () => {
- wrapper.setState({ selected: 'SPECIFIC_ANALYSIS' });
- expect(wrapper.instance().getSettingValue()).toBe('analysis1');
- });
-
- it('should work for Previous version', () => {
- wrapper.setState({ selected: 'PREVIOUS_VERSION' });
- expect(wrapper.instance().getSettingValue()).toBeUndefined();
- });
-});
-
function shallowRender(props: Partial<BranchBaselineSettingModal['props']> = {}) {
return shallow<BranchBaselineSettingModal>(
<BranchBaselineSettingModal
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/ProjectBaselineSelector-test.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/ProjectBaselineSelector-test.tsx
index 87608358874..74384231bf2 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/ProjectBaselineSelector-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/ProjectBaselineSelector-test.tsx
@@ -25,6 +25,7 @@ import ProjectBaselineSelector, {
it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
+ expect(shallowRender({ branchesEnabled: false })).toMatchSnapshot();
});
it('should not show save button when unchanged', () => {
@@ -32,7 +33,12 @@ it('should not show save button when unchanged', () => {
currentSetting: 'PREVIOUS_VERSION',
selected: 'PREVIOUS_VERSION'
});
- expect(wrapper.find('SubmitButton')).toHaveLength(0);
+ expect(
+ wrapper
+ .find('SubmitButton')
+ .parent()
+ .hasClass('invisible')
+ ).toBe(true);
});
it('should show save button when changed', () => {
@@ -84,7 +90,10 @@ it('should disable the save button when date is invalid', () => {
function shallowRender(props: Partial<ProjectBaselineSelectorProps> = {}) {
return shallow(
<ProjectBaselineSelector
+ branchesEnabled={true}
+ component=""
days="12"
+ onSelectAnalysis={jest.fn()}
onSelectDays={jest.fn()}
onSelectSetting={jest.fn()}
onSubmit={jest.fn()}
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/BranchBaselineSettingModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/BranchBaselineSettingModal-test.tsx.snap
index a9710d794a4..e9ffc30753c 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/BranchBaselineSettingModal-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/BranchBaselineSettingModal-test.tsx.snap
@@ -17,7 +17,7 @@ exports[`should render correctly 1`] = `
onSubmit={[Function]}
>
<div
- className="modal-body branch-baseline-setting-modal"
+ className="modal-body modal-container branch-baseline-setting-modal"
>
<div
className="display-flex-row huge-spacer-bottom"
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/ProjectBaselineSelector-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/ProjectBaselineSelector-test.tsx.snap
index c8fe6992237..d938d19be84 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/ProjectBaselineSelector-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/ProjectBaselineSelector-test.tsx.snap
@@ -6,21 +6,96 @@ exports[`should render correctly 1`] = `
onSubmit={[MockFunction]}
>
<div
- className="display-flex-row big-spacer-bottom"
- role="radiogroup"
+ className="branch-baseline-setting-modal"
>
- <BaselineSettingPreviousVersion
- onSelect={[MockFunction]}
- selected={false}
+ <div
+ className="display-flex-row big-spacer-bottom"
+ role="radiogroup"
+ >
+ <BaselineSettingPreviousVersion
+ onSelect={[MockFunction]}
+ selected={false}
+ />
+ <BaselineSettingDays
+ days="12"
+ isChanged={false}
+ isValid={false}
+ onChangeDays={[MockFunction]}
+ onSelect={[MockFunction]}
+ selected={false}
+ />
+ </div>
+ </div>
+ <div
+ className="big-spacer-top invisible"
+ >
+ <p
+ className="spacer-bottom"
+ >
+ baseline.next_analysis_notice
+ </p>
+ <DeferredSpinner
+ className="spacer-right"
+ loading={false}
+ timeout={100}
/>
- <BaselineSettingDays
- days="12"
- isChanged={false}
- isValid={true}
- onChangeDays={[MockFunction]}
- onSelect={[MockFunction]}
- selected={false}
+ <SubmitButton
+ disabled={true}
+ >
+ save
+ </SubmitButton>
+ </div>
+</form>
+`;
+
+exports[`should render correctly 2`] = `
+<form
+ className="project-baseline-selector"
+ onSubmit={[MockFunction]}
+>
+ <div
+ className="branch-baseline-setting-modal"
+ >
+ <div
+ className="display-flex-row big-spacer-bottom"
+ role="radiogroup"
+ >
+ <BaselineSettingPreviousVersion
+ onSelect={[MockFunction]}
+ selected={false}
+ />
+ <BaselineSettingDays
+ days="12"
+ isChanged={false}
+ isValid={false}
+ onChangeDays={[MockFunction]}
+ onSelect={[MockFunction]}
+ selected={false}
+ />
+ <BaselineSettingAnalysis
+ onSelect={[MockFunction]}
+ selected={false}
+ />
+ </div>
+ </div>
+ <div
+ className="big-spacer-top invisible"
+ >
+ <p
+ className="spacer-bottom"
+ >
+ baseline.next_analysis_notice
+ </p>
+ <DeferredSpinner
+ className="spacer-right"
+ loading={false}
+ timeout={100}
/>
+ <SubmitButton
+ disabled={true}
+ >
+ save
+ </SubmitButton>
</div>
</form>
`;
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/utils-test.ts
new file mode 100644
index 00000000000..c77f816b373
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/utils-test.ts
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { getSettingValue } from '../utils';
+
+describe('getSettingValue', () => {
+ it('should work for Days', () => {
+ expect(getSettingValue({ analysis: 'analysis', days: '35', type: 'NUMBER_OF_DAYS' })).toBe(
+ '35'
+ );
+ });
+
+ it('should work for Analysis', () => {
+ expect(getSettingValue({ analysis: 'analysis1', days: '35', type: 'SPECIFIC_ANALYSIS' })).toBe(
+ 'analysis1'
+ );
+ });
+
+ it('should work for Previous version', () => {
+ expect(
+ getSettingValue({ analysis: 'analysis1', days: '35', type: 'PREVIOUS_VERSION' })
+ ).toBeUndefined();
+ });
+});
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx
index c88e2fc9e64..816c0805985 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx
@@ -25,16 +25,19 @@ import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
import { getNewCodePeriod, resetNewCodePeriod, setNewCodePeriod } from '../../../api/newCodePeriod';
import '../styles.css';
+import { getSettingValue } from '../utils';
import BranchList from './BranchList';
import ProjectBaselineSelector from './ProjectBaselineSelector';
interface Props {
branchLikes: T.BranchLike[];
+ branchesEnabled?: boolean;
canAdmin?: boolean;
component: T.Component;
}
interface State {
+ analysis?: string;
currentSetting?: T.NewCodePeriodSettingType;
currentSettingValue?: string;
days: string;
@@ -65,9 +68,34 @@ export default class App extends React.PureComponent<Props, State> {
this.mounted = false;
}
+ getUpdatedState(params: {
+ currentSetting?: T.NewCodePeriodSettingType;
+ currentSettingValue?: string;
+ generalSetting: T.NewCodePeriod;
+ }) {
+ const { currentSetting, currentSettingValue, generalSetting } = params;
+
+ return {
+ loading: false,
+ currentSetting,
+ currentSettingValue,
+ generalSetting,
+ selected: currentSetting,
+ days: currentSetting === 'NUMBER_OF_DAYS' ? currentSettingValue || '30' : '',
+ analysis: (currentSetting === 'SPECIFIC_ANALYSIS' && currentSettingValue) || ''
+ };
+ }
+
fetchLeakPeriodSetting() {
this.setState({ loading: true });
- Promise.all([getNewCodePeriod(), getNewCodePeriod({ project: this.props.component.key })]).then(
+
+ Promise.all([
+ getNewCodePeriod(),
+ getNewCodePeriod({
+ branch: this.props.branchesEnabled ? 'master' : undefined,
+ project: this.props.component.key
+ })
+ ]).then(
([generalSetting, setting]) => {
if (this.mounted) {
if (!generalSetting.type) {
@@ -75,22 +103,10 @@ export default class App extends React.PureComponent<Props, State> {
}
const currentSettingValue = setting.value;
const currentSetting = setting.inherited ? undefined : setting.type || 'PREVIOUS_VERSION';
- const newState = {
- loading: false,
- currentSetting,
- currentSettingValue,
- generalSetting,
- selected: currentSetting
- };
- if (currentSetting === 'NUMBER_OF_DAYS') {
- this.setState({
- days: currentSettingValue || '30',
- ...newState
- });
- } else {
- this.setState(newState);
- }
+ this.setState(
+ this.getUpdatedState({ generalSetting, currentSetting, currentSettingValue })
+ );
}
},
() => {
@@ -115,6 +131,8 @@ export default class App extends React.PureComponent<Props, State> {
);
};
+ handleSelectAnalysis = (analysis: T.ParsedAnalysis) => this.setState({ analysis: analysis.key });
+
handleSelectDays = (days: string) => this.setState({ days });
handleSelectSetting = (selected?: T.NewCodePeriodSettingType) => this.setState({ selected });
@@ -123,10 +141,9 @@ export default class App extends React.PureComponent<Props, State> {
e.preventDefault();
const { component } = this.props;
- const { days, selected } = this.state;
+ const { analysis, days, selected: type } = this.state;
- const type = selected;
- const value = type === 'NUMBER_OF_DAYS' ? days : undefined;
+ const value = getSettingValue({ type, analysis, days });
if (type) {
this.setState({ saving: true });
@@ -196,7 +213,9 @@ export default class App extends React.PureComponent<Props, State> {
}
render() {
+ const { branchLikes, branchesEnabled, component } = this.props;
const {
+ analysis,
currentSetting,
days,
generalSetting,
@@ -213,21 +232,23 @@ export default class App extends React.PureComponent<Props, State> {
<DeferredSpinner />
) : (
<div className="panel panel-white">
- <h2>{translate('project_baseline.default_setting')}</h2>
- <p>{translate('project_baseline.default_setting.description')}</p>
+ {branchesEnabled && (
+ <>
+ <h2>{translate('project_baseline.default_setting')}</h2>
+ <p>{translate('project_baseline.default_setting.description')}</p>
+ </>
+ )}
{generalSetting && (
<div className="text-right spacer-bottom">
{currentSetting && (
<>
- <Button
- className="spacer-right little-spacer-bottom"
- onClick={this.resetSetting}>
+ <Button className="little-spacer-bottom" onClick={this.resetSetting}>
{translate('project_baseline.reset_to_general')}
</Button>
</>
)}
- <div className="spacer-top spacer-right medium">
+ <div className="spacer-top medium">
<strong>{translate('project_baseline.general_setting')}: </strong>
{this.renderGeneralSetting(generalSetting)}
</div>
@@ -235,19 +256,23 @@ export default class App extends React.PureComponent<Props, State> {
)}
<ProjectBaselineSelector
+ analysis={analysis}
+ branchesEnabled={branchesEnabled}
+ component={component.key}
currentSetting={currentSetting}
currentSettingValue={currentSettingValue}
days={days}
+ onSelectAnalysis={this.handleSelectAnalysis}
onSelectDays={this.handleSelectDays}
onSelectSetting={this.handleSelectSetting}
onSubmit={this.handleSubmit}
saving={saving}
selected={selected}
/>
- {generalSetting && (
+ {generalSetting && branchesEnabled && (
<BranchList
- branchLikes={this.props.branchLikes}
- component={this.props.component}
+ branchLikes={branchLikes}
+ component={component}
inheritedSetting={
currentSetting
? {
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/AppContainer.ts b/server/sonar-web/src/main/js/apps/projectBaseline/components/AppContainer.ts
index 10205a7f932..1a38ec7f7f8 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/components/AppContainer.ts
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/AppContainer.ts
@@ -22,6 +22,7 @@ import { getAppState, Store } from '../../../store/rootReducer';
import App from './App';
const mapStateToProps = (state: Store) => ({
+ branchesEnabled: getAppState(state).branchesEnabled,
canAdmin: getAppState(state).canAdmin
});
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisList.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisList.tsx
index 4791f62ba6e..b2ed30fd434 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisList.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisList.tsx
@@ -30,17 +30,17 @@ import { getProjectActivity } from '../../../api/projectActivity';
import DateFormatter from '../../../components/intl/DateFormatter';
import TimeFormatter from '../../../components/intl/TimeFormatter';
import Events from '../../projectActivity/components/Events';
-import { getAnalysesByVersionByDay, ParsedAnalysis } from '../../projectActivity/utils';
+import { getAnalysesByVersionByDay } from '../../projectActivity/utils';
interface Props {
analysis: string;
branch: string;
component: string;
- onSelectAnalysis: (analysis: ParsedAnalysis) => void;
+ onSelectAnalysis: (analysis: T.ParsedAnalysis) => void;
}
interface State {
- analyses: ParsedAnalysis[];
+ analyses: T.ParsedAnalysis[];
loading: boolean;
range: number;
scroll: number;
@@ -90,7 +90,7 @@ export default class BranchAnalysisList extends React.PureComponent<Props, State
analyses: result.analyses.map(analysis => ({
...analysis,
date: parseDate(analysis.date)
- })) as ParsedAnalysis[],
+ })) as T.ParsedAnalysis[],
loading: false
});
});
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchBaselineSettingModal.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchBaselineSettingModal.tsx
index b8dd08c6b66..0a180907f92 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchBaselineSettingModal.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchBaselineSettingModal.tsx
@@ -24,8 +24,7 @@ import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
import { toNotSoISOString } from 'sonar-ui-common/helpers/dates';
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
import { setNewCodePeriod } from '../../../api/newCodePeriod';
-import { ParsedAnalysis } from '../../projectActivity/utils';
-import { validateDays } from '../utils';
+import { getSettingValue, validateSetting } from '../utils';
import BaselineSettingAnalysis from './BaselineSettingAnalysis';
import BaselineSettingDays from './BaselineSettingDays';
import BaselineSettingPreviousVersion from './BaselineSettingPreviousVersion';
@@ -73,24 +72,13 @@ export default class BranchBaselineSettingModal extends React.PureComponent<Prop
: null;
}
- getSettingValue() {
- switch (this.state.selected) {
- case 'NUMBER_OF_DAYS':
- return this.state.days;
- case 'SPECIFIC_ANALYSIS':
- return this.state.analysis;
- default:
- return undefined;
- }
- }
-
handleSubmit = (e: React.SyntheticEvent<HTMLFormElement>) => {
e.preventDefault();
const { branch, component } = this.props;
- const { analysisDate, selected: type } = this.state;
+ const { analysis, analysisDate, days, selected: type } = this.state;
- const value = this.getSettingValue();
+ const value = getSettingValue({ type, analysis, days });
if (type) {
this.setState({ saving: true });
@@ -121,7 +109,7 @@ export default class BranchBaselineSettingModal extends React.PureComponent<Prop
requestClose = () => this.props.onClose();
- handleSelectAnalysis = (analysis: ParsedAnalysis) =>
+ handleSelectAnalysis = (analysis: T.ParsedAnalysis) =>
this.setState({ analysis: analysis.key, analysisDate: analysis.date });
handleSelectDays = (days: string) => this.setState({ days });
@@ -132,20 +120,18 @@ export default class BranchBaselineSettingModal extends React.PureComponent<Prop
const { branch } = this.props;
const { analysis, days, saving, selected } = this.state;
- const currentSetting = branch.newCodePeriod && branch.newCodePeriod.type;
- const currentSettingValue = branch.newCodePeriod && branch.newCodePeriod.value;
-
const header = translateWithParameters('baseline.new_code_period_for_branch_x', branch.name);
- const isChanged =
- selected !== currentSetting ||
- (selected === 'NUMBER_OF_DAYS' && String(days) !== currentSettingValue) ||
- (selected === 'SPECIFIC_ANALYSIS' && analysis !== currentSettingValue);
+ const currentSetting = branch.newCodePeriod && branch.newCodePeriod.type;
+ const currentSettingValue = branch.newCodePeriod && branch.newCodePeriod.value;
- const isValid =
- selected === 'PREVIOUS_VERSION' ||
- (selected === 'SPECIFIC_ANALYSIS' && analysis.length > 0) ||
- (selected === 'NUMBER_OF_DAYS' && validateDays(days));
+ const { isChanged, isValid } = validateSetting({
+ analysis,
+ currentSetting,
+ currentSettingValue,
+ days,
+ selected
+ });
return (
<Modal contentLabel={header} onRequestClose={this.requestClose} size="large">
@@ -153,7 +139,7 @@ export default class BranchBaselineSettingModal extends React.PureComponent<Prop
<h2>{header}</h2>
</header>
<form onSubmit={this.handleSubmit}>
- <div className="modal-body branch-baseline-setting-modal">
+ <div className="modal-body modal-container branch-baseline-setting-modal">
<div className="display-flex-row huge-spacer-bottom" role="radiogroup">
<BaselineSettingPreviousVersion
isDefault={false}
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineSelector.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineSelector.tsx
index a29dfb03eba..5aabd1ad5ac 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineSelector.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineSelector.tsx
@@ -17,18 +17,25 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import * as classNames from 'classnames';
import * as React from 'react';
import { SubmitButton } from 'sonar-ui-common/components/controls/buttons';
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
import { translate } from 'sonar-ui-common/helpers/l10n';
-import { validateDays } from '../utils';
+import { validateSetting } from '../utils';
+import BaselineSettingAnalysis from './BaselineSettingAnalysis';
import BaselineSettingDays from './BaselineSettingDays';
import BaselineSettingPreviousVersion from './BaselineSettingPreviousVersion';
+import BranchAnalysisList from './BranchAnalysisList';
export interface ProjectBaselineSelectorProps {
+ analysis?: string;
+ branchesEnabled?: boolean;
+ component: string;
currentSetting?: T.NewCodePeriodSettingType;
- currentSettingValue?: string | number;
+ currentSettingValue?: string;
days: string;
+ onSelectAnalysis: (analysis: T.ParsedAnalysis) => void;
onSelectDays: (value: string) => void;
onSelectSetting: (value: T.NewCodePeriodSettingType) => void;
onSubmit: (e: React.SyntheticEvent<HTMLFormElement>) => void;
@@ -37,37 +44,62 @@ export interface ProjectBaselineSelectorProps {
}
export default function ProjectBaselineSelector(props: ProjectBaselineSelectorProps) {
- const { currentSetting, days, currentSettingValue, saving, selected } = props;
+ const {
+ analysis,
+ branchesEnabled,
+ component,
+ currentSetting,
+ days,
+ currentSettingValue,
+ saving,
+ selected
+ } = props;
- const isChanged =
- selected !== currentSetting ||
- (selected === 'NUMBER_OF_DAYS' && String(days) !== currentSettingValue);
-
- const isValid = selected !== 'NUMBER_OF_DAYS' || validateDays(days);
+ const { isChanged, isValid } = validateSetting({
+ analysis,
+ currentSetting,
+ currentSettingValue,
+ days,
+ selected
+ });
return (
<form className="project-baseline-selector" onSubmit={props.onSubmit}>
- <div className="display-flex-row big-spacer-bottom" role="radiogroup">
- <BaselineSettingPreviousVersion
- onSelect={props.onSelectSetting}
- selected={selected === 'PREVIOUS_VERSION'}
- />
- <BaselineSettingDays
- days={days}
- isChanged={isChanged}
- isValid={isValid}
- onChangeDays={props.onSelectDays}
- onSelect={props.onSelectSetting}
- selected={selected === 'NUMBER_OF_DAYS'}
- />
- </div>
- {isChanged && (
- <div>
- <p className="spacer-bottom">{translate('baseline.next_analysis_notice')}</p>
- <DeferredSpinner className="spacer-right" loading={saving} />
- <SubmitButton disabled={saving || !isValid}>{translate('save')}</SubmitButton>
+ <div className="branch-baseline-setting-modal">
+ <div className="display-flex-row big-spacer-bottom" role="radiogroup">
+ <BaselineSettingPreviousVersion
+ onSelect={props.onSelectSetting}
+ selected={selected === 'PREVIOUS_VERSION'}
+ />
+ <BaselineSettingDays
+ days={days}
+ isChanged={isChanged}
+ isValid={isValid}
+ onChangeDays={props.onSelectDays}
+ onSelect={props.onSelectSetting}
+ selected={selected === 'NUMBER_OF_DAYS'}
+ />
+ {!branchesEnabled && (
+ <BaselineSettingAnalysis
+ onSelect={props.onSelectSetting}
+ selected={selected === 'SPECIFIC_ANALYSIS'}
+ />
+ )}
</div>
- )}
+ {selected === 'SPECIFIC_ANALYSIS' && (
+ <BranchAnalysisList
+ analysis={analysis || ''}
+ branch="master"
+ component={component}
+ onSelectAnalysis={props.onSelectAnalysis}
+ />
+ )}
+ </div>
+ <div className={classNames('big-spacer-top', { invisible: !isChanged })}>
+ <p className="spacer-bottom">{translate('baseline.next_analysis_notice')}</p>
+ <DeferredSpinner className="spacer-right" loading={saving} />
+ <SubmitButton disabled={saving || !isValid || !isChanged}>{translate('save')}</SubmitButton>
+ </div>
</form>
);
}
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/styles.css b/server/sonar-web/src/main/js/apps/projectBaseline/styles.css
index 06b52b4a9bc..9bffdfa35cc 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/styles.css
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/styles.css
@@ -18,13 +18,12 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-.project-baseline-selector {
- height: 300px;
+.project-baseline-selector > .branch-baseline-setting-modal {
+ max-height: 60vh;
+ padding-top: 2px;
}
.branch-baseline-setting-modal {
- height: 60vh;
- overflow: hidden;
display: flex;
flex-direction: column;
}
@@ -34,6 +33,7 @@
flex-direction: column;
overflow: hidden;
position: relative;
+ min-height: 200px;
}
.branch-analysis-list {
@@ -100,10 +100,6 @@
z-index: var(--belowNormalZIndex);
}
-.branch-analysis-version-badge.sticky + .branch-analysis-days-list {
- padding-top: 36px;
-}
-
.branch-analysis-version-badge .badge {
max-width: 385px;
border-radius: 0 2px 2px 0;
@@ -133,3 +129,7 @@
.project-activity-event-icon.OTHER {
color: #442d1b;
}
+
+.invisible {
+ visibility: hidden;
+}
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/utils.ts b/server/sonar-web/src/main/js/apps/projectBaseline/utils.ts
index afaa962b3dc..69b7cfabda0 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/utils.ts
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/utils.ts
@@ -22,3 +22,44 @@ export function validateDays(days: string) {
return !(days.length < 1 || isNaN(parsed) || parsed < 1 || String(parsed) !== days);
}
+
+export function getSettingValue({
+ analysis,
+ days,
+ type
+}: {
+ analysis?: string;
+ days?: string;
+ type?: T.NewCodePeriodSettingType;
+}) {
+ switch (type) {
+ case 'NUMBER_OF_DAYS':
+ return days;
+ case 'SPECIFIC_ANALYSIS':
+ return analysis;
+ default:
+ return undefined;
+ }
+}
+
+export function validateSetting(state: {
+ analysis?: string;
+ currentSetting?: T.NewCodePeriodSettingType;
+ currentSettingValue?: string;
+ days: string;
+ selected?: T.NewCodePeriodSettingType;
+}) {
+ const { analysis = '', currentSetting, currentSettingValue, days, selected } = state;
+
+ const isChanged =
+ selected !== currentSetting ||
+ (selected === 'NUMBER_OF_DAYS' && days !== currentSettingValue) ||
+ (selected === 'SPECIFIC_ANALYSIS' && analysis !== currentSettingValue);
+
+ const isValid =
+ selected === 'PREVIOUS_VERSION' ||
+ (selected === 'SPECIFIC_ANALYSIS' && analysis.length > 0) ||
+ (selected === 'NUMBER_OF_DAYS' && validateDays(days));
+
+ return { isChanged, isValid };
+}
diff --git a/server/sonar-web/src/main/js/helpers/testMocks.ts b/server/sonar-web/src/main/js/helpers/testMocks.ts
index 0b159181b44..c667b17d2d1 100644
--- a/server/sonar-web/src/main/js/helpers/testMocks.ts
+++ b/server/sonar-web/src/main/js/helpers/testMocks.ts
@@ -22,7 +22,6 @@ import { Location } from 'history';
import { InjectedRouter } from 'react-router';
import { createStore, Store } from 'redux';
import { DocumentationEntry } from '../apps/documentation/utils';
-import { ParsedAnalysis } from '../apps/projectActivity/utils';
import { Profile } from '../apps/quality-profiles/types';
export function mockAlmApplication(overrides: Partial<T.AlmApplication> = {}): T.AlmApplication {
@@ -61,7 +60,7 @@ export function mockAnalysis(overrides: Partial<T.Analysis> = {}): T.Analysis {
};
}
-export function mockParsedAnalysis(overrides: Partial<ParsedAnalysis> = {}): ParsedAnalysis {
+export function mockParsedAnalysis(overrides: Partial<T.ParsedAnalysis> = {}): T.ParsedAnalysis {
return {
date: new Date('2017-03-01T09:37:01+0100'),
events: [],