3 * Copyright (C) 2009-2023 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 import { within } from '@testing-library/react';
21 import userEvent from '@testing-library/user-event';
22 import { first, last } from 'lodash';
23 import selectEvent from 'react-select-event';
24 import BranchesServiceMock from '../../../../api/mocks/BranchesServiceMock';
25 import NewCodeDefinitionServiceMock from '../../../../api/mocks/NewCodeDefinitionServiceMock';
26 import { ProjectActivityServiceMock } from '../../../../api/mocks/ProjectActivityServiceMock';
27 import { mockComponent } from '../../../../helpers/mocks/component';
28 import { mockNewCodePeriodBranch } from '../../../../helpers/mocks/new-code-definition';
29 import { mockAppState } from '../../../../helpers/testMocks';
32 renderAppWithComponentContext,
33 } from '../../../../helpers/testReactTestingUtils';
34 import { byRole, byText } from '../../../../helpers/testSelector';
35 import { Feature } from '../../../../types/features';
36 import { NewCodeDefinitionType } from '../../../../types/new-code-definition';
37 import routes from '../../routes';
39 jest.mock('../../../../api/newCodeDefinition');
40 jest.mock('../../../../api/projectActivity');
41 jest.mock('../../../../api/branches');
43 const codePeriodsMock = new NewCodeDefinitionServiceMock();
44 const projectActivityMock = new ProjectActivityServiceMock();
45 const branchHandler = new BranchesServiceMock();
48 branchHandler.reset();
49 codePeriodsMock.reset();
50 projectActivityMock.reset();
53 it('renders correctly without branch support feature', async () => {
54 const { ui } = getPageObjects();
55 renderProjectBaselineApp();
56 await ui.appIsLoaded();
58 expect(await ui.generalSettingRadio.find()).toBeChecked();
59 expect(ui.specificAnalysisRadio.query()).not.toBeInTheDocument();
62 expect(ui.generalSettingsLink.query()).not.toBeInTheDocument();
64 // Specific branch setting is not rendered without feature branch
65 expect(ui.branchListHeading.query()).not.toBeInTheDocument();
66 expect(ui.referenceBranchRadio.query()).not.toBeInTheDocument();
69 it('prevents selection of global setting if it is not compliant and warns non-admin about it', async () => {
70 codePeriodsMock.setNewCodePeriod({
71 type: NewCodeDefinitionType.NumberOfDays,
76 const { ui } = getPageObjects();
77 renderProjectBaselineApp();
78 await ui.appIsLoaded();
80 expect(await ui.generalSettingRadio.find()).toBeChecked();
81 expect(ui.generalSettingRadio.get()).toBeDisabled();
82 expect(ui.complianceWarning.get()).toBeVisible();
85 it('prevents selection of global setting if it is not compliant and warns admin about it', async () => {
86 codePeriodsMock.setNewCodePeriod({
87 type: NewCodeDefinitionType.NumberOfDays,
92 const { ui } = getPageObjects();
93 renderProjectBaselineApp({ appState: mockAppState({ canAdmin: true }) });
94 await ui.appIsLoaded();
96 expect(await ui.generalSettingRadio.find()).toBeChecked();
97 expect(ui.generalSettingRadio.get()).toBeDisabled();
98 expect(ui.complianceWarningAdmin.get()).toBeVisible();
99 expect(ui.complianceWarning.query()).not.toBeInTheDocument();
102 it('renders correctly with branch support feature', async () => {
103 const { ui } = getPageObjects();
104 renderProjectBaselineApp({
105 featureList: [Feature.BranchSupport],
106 appState: mockAppState({ canAdmin: true }),
108 await ui.appIsLoaded();
110 expect(await ui.generalSettingRadio.find()).toBeChecked();
111 expect(ui.specificAnalysisRadio.query()).not.toBeInTheDocument();
114 expect(ui.generalSettingsLink.get()).toBeInTheDocument();
116 // Specific branch setting is rendered with feature support branch
117 expect(ui.branchListHeading.get()).toBeInTheDocument();
118 expect(ui.referenceBranchRadio.get()).toBeInTheDocument();
121 it('can set previous version specific setting', async () => {
122 const { ui, user } = getPageObjects();
123 renderProjectBaselineApp();
124 await ui.appIsLoaded();
126 expect(await ui.previousVersionRadio.find()).toHaveClass('disabled');
127 await ui.setPreviousVersionSetting();
128 expect(ui.previousVersionRadio.get()).toBeChecked();
131 await user.click(ui.saveButton.get());
133 expect(ui.saved.get()).toBeInTheDocument();
135 // Set general setting
136 await user.click(ui.generalSettingRadio.get());
137 expect(ui.previousVersionRadio.get()).toHaveClass('disabled');
138 await user.click(ui.saveButton.get());
139 expect(ui.saved.get()).toBeInTheDocument();
142 it('can set number of days specific setting', async () => {
143 const { ui, user } = getPageObjects();
144 renderProjectBaselineApp();
145 await ui.appIsLoaded();
147 expect(await ui.numberDaysRadio.find()).toHaveClass('disabled');
148 await ui.setNumberDaysSetting('10');
149 expect(ui.numberDaysRadio.get()).toBeChecked();
151 // Reset to initial state
152 await user.click(ui.cancelButton.get());
153 expect(ui.generalSettingRadio.get()).toBeChecked();
154 expect(ui.numberDaysRadio.get()).toHaveClass('disabled');
157 await ui.setNumberDaysSetting('10');
158 await user.click(ui.saveButton.get());
160 expect(ui.saved.get()).toBeInTheDocument();
163 it('can set reference branch specific setting', async () => {
164 const { ui, user } = getPageObjects();
165 renderProjectBaselineApp({
166 featureList: [Feature.BranchSupport],
168 await ui.appIsLoaded();
170 expect(await ui.referenceBranchRadio.find()).toHaveClass('disabled');
171 await ui.setReferenceBranchSetting('main');
172 expect(ui.referenceBranchRadio.get()).toBeChecked();
175 await user.click(ui.saveButton.get());
177 expect(ui.saved.get()).toBeInTheDocument();
180 it('cannot set specific analysis setting', async () => {
181 const { ui } = getPageObjects();
182 codePeriodsMock.setNewCodePeriod({
183 type: NewCodeDefinitionType.SpecificAnalysis,
184 value: 'analysis_id',
186 renderProjectBaselineApp();
187 await ui.appIsLoaded();
189 expect(await ui.specificAnalysisRadio.find()).toBeChecked();
190 expect(ui.specificAnalysisRadio.get()).toHaveClass('disabled');
191 expect(ui.specificAnalysisWarning.get()).toBeInTheDocument();
193 await selectEvent.select(ui.analysisFromSelect.get(), 'baseline.branch_analyses.ranges.allTime');
195 expect(first(ui.analysisListItem.getAll())).toHaveClass('disabled');
196 expect(ui.saveButton.get()).toBeDisabled();
199 it('renders correctly branch modal', async () => {
200 const { ui } = getPageObjects();
201 renderProjectBaselineApp({
202 featureList: [Feature.BranchSupport],
204 await ui.appIsLoaded();
206 await ui.openBranchSettingModal('main');
208 expect(ui.specificAnalysisRadio.query()).not.toBeInTheDocument();
211 it('can set a previous version setting for branch', async () => {
212 const { ui, user } = getPageObjects();
213 renderProjectBaselineApp({
214 featureList: [Feature.BranchSupport],
216 await ui.appIsLoaded();
217 await ui.setBranchPreviousVersionSetting('main');
220 within(byRole('table').get()).getByText('new_code_definition.previous_version')
221 ).toBeInTheDocument();
223 await user.click(await ui.branchActionsButton('main').find());
225 expect(ui.resetToDefaultButton.get()).toBeInTheDocument();
226 await user.click(ui.resetToDefaultButton.get());
229 first(within(byRole('table').get()).getAllByText('branch_list.default_setting'))
230 ).toBeInTheDocument();
233 it('can set a number of days setting for branch', async () => {
234 const { ui } = getPageObjects();
235 renderProjectBaselineApp({
236 featureList: [Feature.BranchSupport],
238 await ui.appIsLoaded();
240 await ui.setBranchNumberOfDaysSetting('main', '15');
243 within(byRole('table').get()).getByText('new_code_definition.number_days: 15')
244 ).toBeInTheDocument();
247 it('cannot set a specific analysis setting for branch', async () => {
248 const { ui } = getPageObjects();
249 codePeriodsMock.setListBranchesNewCode([
250 mockNewCodePeriodBranch({
252 type: NewCodeDefinitionType.SpecificAnalysis,
253 value: 'analysis_id',
256 renderProjectBaselineApp({
257 featureList: [Feature.BranchSupport],
259 await ui.appIsLoaded();
261 await ui.openBranchSettingModal('main');
263 expect(ui.specificAnalysisRadio.get()).toBeChecked();
264 expect(ui.specificAnalysisRadio.get()).toHaveClass('disabled');
265 expect(ui.specificAnalysisWarning.get()).toBeInTheDocument();
267 await selectEvent.select(ui.analysisFromSelect.get(), 'baseline.branch_analyses.ranges.allTime');
269 expect(first(ui.analysisListItem.getAll())).toHaveClass('disabled');
270 expect(last(ui.saveButton.getAll())).toBeDisabled();
273 it('can set a reference branch setting for branch', async () => {
274 const { ui } = getPageObjects();
275 renderProjectBaselineApp({
276 featureList: [Feature.BranchSupport],
278 await ui.appIsLoaded();
280 await ui.setBranchReferenceToBranchSetting('main', 'normal-branch');
283 byRole('table').byText('baseline.reference_branch: normal-branch').get()
284 ).toBeInTheDocument();
287 function renderProjectBaselineApp(context: RenderContext = {}, params?: string) {
288 return renderAppWithComponentContext(
293 navigateTo: params ? `baseline?id=my-project&${params}` : 'baseline?id=my-project',
296 component: mockComponent(),
301 function getPageObjects() {
302 const user = userEvent.setup();
305 pageHeading: byRole('heading', { name: 'project_baseline.page' }),
306 branchTableHeading: byText('branch_list.branch'),
307 branchListHeading: byRole('heading', { name: 'project_baseline.default_setting' }),
308 generalSettingsLink: byRole('link', { name: 'project_baseline.page.description2.link' }),
309 generalSettingRadio: byRole('radio', { name: 'project_baseline.global_setting' }),
310 specificSettingRadio: byRole('radio', { name: 'project_baseline.specific_setting' }),
311 previousVersionRadio: byRole('radio', {
312 name: /new_code_definition.previous_version.description/,
314 numberDaysRadio: byRole('radio', { name: /new_code_definition.number_days.description/ }),
315 numberDaysInput: byRole('spinbutton'),
316 referenceBranchRadio: byRole('radio', { name: /baseline.reference_branch.description/ }),
317 chooseBranchSelect: byRole('combobox', { name: 'baseline.reference_branch.choose' }),
318 specificAnalysisRadio: byRole('radio', { name: /baseline.specific_analysis.description/ }),
319 specificAnalysisWarning: byText('baseline.specific_analysis.compliance_warning.title'),
320 analysisFromSelect: byRole('combobox', { name: 'baseline.analysis_from' }),
321 analysisListItem: byRole('radio', { name: /baseline.branch_analyses.analysis_for_x/ }),
322 saveButton: byRole('button', { name: 'save' }),
323 cancelButton: byRole('button', { name: 'cancel' }),
324 branchActionsButton: (branch: string) =>
325 byRole('button', { name: `branch_list.show_actions_for_x.${branch}` }),
326 editButton: byRole('button', { name: 'edit' }),
327 resetToDefaultButton: byRole('button', { name: 'reset_to_default' }),
328 saved: byText('settings.state.saved'),
329 complianceWarningAdmin: byText('new_code_definition.compliance.warning.explanation.admin'),
330 complianceWarning: byText('new_code_definition.compliance.warning.explanation'),
333 async function appIsLoaded() {
334 expect(await ui.pageHeading.find()).toBeInTheDocument();
337 async function setPreviousVersionSetting() {
338 await user.click(ui.specificSettingRadio.get());
339 await user.click(ui.previousVersionRadio.get());
342 async function setBranchPreviousVersionSetting(branch: string) {
343 await openBranchSettingModal(branch);
344 await user.click(last(ui.previousVersionRadio.getAll()) as HTMLElement);
345 await user.click(last(ui.saveButton.getAll()) as HTMLElement);
348 async function setNumberDaysSetting(value: string) {
349 await user.click(ui.specificSettingRadio.get());
350 await user.click(ui.numberDaysRadio.get());
351 await user.clear(ui.numberDaysInput.get());
352 await user.type(ui.numberDaysInput.get(), value);
355 async function setBranchNumberOfDaysSetting(branch: string, value: string) {
356 await openBranchSettingModal(branch);
357 await user.click(last(ui.numberDaysRadio.getAll()) as HTMLElement);
358 await user.clear(ui.numberDaysInput.get());
359 await user.type(ui.numberDaysInput.get(), value);
360 await user.click(last(ui.saveButton.getAll()) as HTMLElement);
363 async function setReferenceBranchSetting(branch: string) {
364 await user.click(ui.specificSettingRadio.get());
365 await user.click(ui.referenceBranchRadio.get());
366 await selectEvent.select(ui.chooseBranchSelect.get(), branch);
369 async function setBranchReferenceToBranchSetting(branch: string, branchRef: string) {
370 await openBranchSettingModal(branch);
371 await user.click(last(ui.referenceBranchRadio.getAll()) as HTMLElement);
372 await selectEvent.select(ui.chooseBranchSelect.get(), branchRef);
373 await user.click(last(ui.saveButton.getAll()) as HTMLElement);
376 async function openBranchSettingModal(branch: string) {
377 await user.click(await ui.branchActionsButton(branch).find());
378 await user.click(ui.editButton.get());
385 setNumberDaysSetting,
386 setPreviousVersionSetting,
387 setReferenceBranchSetting,
388 setBranchPreviousVersionSetting,
389 setBranchNumberOfDaysSetting,
390 setBranchReferenceToBranchSetting,
391 openBranchSettingModal,