123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- /*
- * SonarQube
- * Copyright (C) 2009-2024 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 { waitFor } from '@testing-library/react';
- import userEvent from '@testing-library/user-event';
- import { UserEvent } from '@testing-library/user-event/dist/types/setup/setup';
- import AlmSettingsServiceMock from '../../../../api/mocks/AlmSettingsServiceMock';
- import DopTranslationServiceMock from '../../../../api/mocks/DopTranslationServiceMock';
- import NewCodeDefinitionServiceMock from '../../../../api/mocks/NewCodeDefinitionServiceMock';
- import { ProjectsServiceMock } from '../../../../api/mocks/ProjectsServiceMock';
- import { getNewCodeDefinition } from '../../../../api/newCodeDefinition';
- import { mockProject } from '../../../../helpers/mocks/projects';
- import { mockAppState, mockCurrentUser } from '../../../../helpers/testMocks';
- import { renderAppRoutes } from '../../../../helpers/testReactTestingUtils';
- import { byRole, byText } from '../../../../helpers/testSelector';
- import { NewCodeDefinitionType } from '../../../../types/new-code-definition';
- import { Permissions } from '../../../../types/permissions';
- import routes from '../../../projects/routes';
-
- jest.mock('../../../../api/measures');
- jest.mock('../../../../api/favorites');
- jest.mock('../../../../api/alm-settings');
- jest.mock('../../../../api/dop-translation');
- jest.mock('../../../../api/newCodeDefinition');
- jest.mock('../../../../api/project-management', () => ({
- createProject: jest.fn().mockReturnValue(Promise.resolve({ project: mockProject() })),
- }));
- jest.mock('../../../../api/components', () => ({
- ...jest.requireActual('../../../../api/components'),
- searchProjects: jest.fn(),
- getScannableProjects: jest.fn(),
- doesComponentExists: jest
- .fn()
- .mockImplementation(({ component }) => Promise.resolve(component === 'exists')),
- }));
- jest.mock('../../../../api/settings', () => ({
- getValue: jest.fn().mockResolvedValue({ value: 'main' }),
- }));
-
- const ui = {
- manualCreateProjectOption: byText('onboarding.create_project.select_method.manual'),
- manualProjectHeader: byText('onboarding.create_project.manual.title'),
- displayNameField: byRole('textbox', {
- name: /onboarding.create_project.display_name/,
- }),
- projectNextButton: byRole('button', { name: 'next' }),
- newCodeDefinitionSection: byRole('region', {
- name: 'onboarding.create_project.new_code_definition.title',
- }),
- newCodeDefinitionHeader: byText('onboarding.create_x_project.new_code_definition.title1'),
- inheritGlobalNcdRadio: byRole('radio', { name: 'new_code_definition.global_setting' }),
- projectCreateButton: byRole('button', {
- name: 'onboarding.create_project.new_code_definition.create_x_projects1',
- }),
- cancelButton: byRole('button', { name: 'cancel' }),
- closeButton: byRole('button', { name: 'clear' }),
- createProjectsButton: byRole('button', { name: 'projects.add' }),
- createLocalProject: byRole('menuitem', { name: 'my_account.add_project.manual' }),
- overrideNcdRadio: byRole('radio', { name: 'new_code_definition.specific_setting' }),
- ncdOptionPreviousVersionRadio: byRole('radio', {
- name: /new_code_definition.previous_version/,
- }),
- ncdOptionRefBranchRadio: byRole('radio', {
- name: /new_code_definition.reference_branch/,
- }),
- ncdOptionDaysRadio: byRole('radio', {
- name: /new_code_definition.number_days/,
- }),
- ncdOptionDaysInput: byRole('spinbutton', {
- name: /new_code_definition.number_days.specify_days/,
- }),
- ncdOptionDaysInputError: byText('new_code_definition.number_days.invalid.1.90'),
- projectDashboardText: byText('/dashboard?id=foo'),
- projectsPageTitle: byRole('heading', { name: 'projects.page' }),
- };
-
- async function fillFormAndNext(displayName: string, user: UserEvent) {
- expect(ui.manualProjectHeader.get()).toBeInTheDocument();
-
- await user.click(ui.displayNameField.get());
- await user.keyboard(displayName);
-
- expect(ui.projectNextButton.get()).toBeEnabled();
- await user.click(ui.projectNextButton.get());
- }
-
- let almSettingsHandler: AlmSettingsServiceMock;
- let dopTranslationHandler: DopTranslationServiceMock;
- let newCodePeriodHandler: NewCodeDefinitionServiceMock;
- let projectHandler: ProjectsServiceMock;
-
- const original = window.location;
-
- beforeAll(() => {
- Object.defineProperty(window, 'location', {
- configurable: true,
- value: { replace: jest.fn() },
- });
- almSettingsHandler = new AlmSettingsServiceMock();
- dopTranslationHandler = new DopTranslationServiceMock();
- newCodePeriodHandler = new NewCodeDefinitionServiceMock();
- projectHandler = new ProjectsServiceMock();
- });
-
- beforeEach(() => {
- jest.clearAllMocks();
- almSettingsHandler.reset();
- dopTranslationHandler.reset();
- newCodePeriodHandler.reset();
- projectHandler.reset();
- });
-
- afterAll(() => {
- Object.defineProperty(window, 'location', { configurable: true, value: original });
- });
-
- it('should fill form and move to NCD selection', async () => {
- const user = userEvent.setup();
- renderCreateProject();
- await fillFormAndNext('test', user);
-
- expect(ui.newCodeDefinitionHeader.get()).toBeInTheDocument();
- });
-
- it('should select the global NCD when it is compliant', async () => {
- jest
- .mocked(getNewCodeDefinition)
- .mockResolvedValue({ type: NewCodeDefinitionType.NumberOfDays, value: '30' });
- const user = userEvent.setup();
- renderCreateProject();
- await fillFormAndNext('test', user);
-
- expect(ui.newCodeDefinitionHeader.get()).toBeInTheDocument();
- expect(ui.inheritGlobalNcdRadio.get()).toBeInTheDocument();
- expect(ui.inheritGlobalNcdRadio.get()).toBeEnabled();
- expect(ui.projectCreateButton.get()).toBeDisabled();
-
- await user.click(ui.inheritGlobalNcdRadio.get());
-
- expect(ui.projectCreateButton.get()).toBeEnabled();
- });
-
- it('number of days ignores non-numeric inputs', async () => {
- jest
- .mocked(getNewCodeDefinition)
- .mockResolvedValue({ type: NewCodeDefinitionType.NumberOfDays, value: '60' });
- const user = userEvent.setup();
- renderCreateProject();
- await fillFormAndNext('test', user);
-
- expect(ui.projectCreateButton.get()).toBeDisabled();
- expect(ui.overrideNcdRadio.get()).not.toHaveClass('disabled');
- expect(ui.ncdOptionDaysRadio.get()).toHaveClass('disabled');
-
- await user.click(ui.overrideNcdRadio.get());
- expect(ui.ncdOptionDaysRadio.get()).not.toHaveClass('disabled');
-
- await user.click(ui.ncdOptionDaysRadio.get());
-
- expect(ui.ncdOptionDaysInput.get()).toBeInTheDocument();
- expect(ui.ncdOptionDaysInput.get()).toHaveValue(60);
- expect(ui.projectCreateButton.get()).toBeEnabled();
-
- await user.click(ui.ncdOptionDaysInput.get());
- await user.keyboard('abc');
-
- // it ignores the input and preserves its value
- expect(ui.ncdOptionDaysInput.get()).toHaveValue(60);
- });
-
- it('the project onboarding page should be displayed when the project is created', async () => {
- newCodePeriodHandler.setNewCodePeriod({ type: NewCodeDefinitionType.NumberOfDays });
- const user = userEvent.setup();
- renderCreateProject();
- await fillFormAndNext('testing', user);
-
- await user.click(ui.overrideNcdRadio.get());
-
- expect(ui.projectCreateButton.get()).toBeEnabled();
- await user.click(ui.projectCreateButton.get());
-
- expect(await ui.projectDashboardText.find()).toBeInTheDocument();
- });
-
- it('validate the private key field', async () => {
- const user = userEvent.setup();
- renderCreateProject();
- expect(ui.manualProjectHeader.get()).toBeInTheDocument();
-
- await user.click(ui.displayNameField.get());
- await user.keyboard('exists');
-
- await waitFor(() => {
- expect(ui.projectNextButton.get()).toBeDisabled();
- });
- await user.click(ui.projectNextButton.get());
- });
-
- it('should navigate back to the Projects page when clicking cancel or close', async () => {
- newCodePeriodHandler.setNewCodePeriod({ type: NewCodeDefinitionType.NumberOfDays });
- const user = userEvent.setup();
- renderCreateProject();
-
- await user.click(ui.cancelButton.get());
- expect(await ui.projectsPageTitle.find()).toBeInTheDocument();
-
- await user.click(ui.createProjectsButton.get());
- await user.click(await ui.createLocalProject.find());
-
- await user.click(ui.closeButton.get());
- expect(await ui.projectsPageTitle.find()).toBeInTheDocument();
-
- await user.click(ui.createProjectsButton.get());
- await user.click(await ui.createLocalProject.find());
-
- expect(await ui.manualProjectHeader.find()).toBeInTheDocument();
- await fillFormAndNext('testing', user);
- expect(ui.newCodeDefinitionHeader.get()).toBeInTheDocument();
-
- await user.click(await ui.newCodeDefinitionSection.byRole('button', { name: 'clear' }).find());
- expect(await ui.projectsPageTitle.find()).toBeInTheDocument();
- });
-
- function renderCreateProject() {
- renderAppRoutes('projects/create?mode=manual', routes, {
- currentUser: mockCurrentUser({
- permissions: { global: [Permissions.ProjectCreation] },
- }),
- appState: mockAppState({ canAdmin: true }),
- });
- }
|