diff options
author | Sarath Nair <91882341+sarath-nair-sonarsource@users.noreply.github.com> | 2024-08-15 09:20:11 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-08-19 20:02:45 +0000 |
commit | b335ed544ad5468e14c2ef0173373a97adfaf128 (patch) | |
tree | 5bb807afaf7111af721b34044cb7cbb8b83c3dd0 /server | |
parent | 6206c40f5a8b6115f3605d18a045e144caaf5d99 (diff) | |
download | sonarqube-b335ed544ad5468e14c2ef0173373a97adfaf128.tar.gz sonarqube-b335ed544ad5468e14c2ef0173373a97adfaf128.zip |
SONAR-22559 Not apply permission templates on GitLab projects
Diffstat (limited to 'server')
9 files changed, 189 insertions, 56 deletions
diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/Header.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/Header.tsx index 633d1f0b87d..0b2000d8b58 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/Header.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/Header.tsx @@ -18,16 +18,16 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { Button, ButtonVariety } from '@sonarsource/echoes-react'; -import { FlagMessage, Spinner, Title } from 'design-system'; +import { Spinner, Title } from 'design-system'; import React, { useState } from 'react'; import { withRouter } from '~sonar-aligned/components/hoc/withRouter'; import { throwGlobalError } from '~sonar-aligned/helpers/error'; import { Router } from '~sonar-aligned/types/router'; import { createPermissionTemplate } from '../../../api/permissions'; import { translate } from '../../../helpers/l10n'; -import { useGithubProvisioningEnabledQuery } from '../../../queries/identity-provider/github'; import { PERMISSION_TEMPLATES_PATH } from '../utils'; import Form from './Form'; +import ProvisioningWarning from './ProvisioningWarning'; interface Props { ready?: boolean; @@ -38,7 +38,6 @@ interface Props { function Header(props: Props) { const { ready, router } = props; const [createModal, setCreateModal] = useState(false); - const { data: gitHubProvisioningStatus } = useGithubProvisioningEnabledQuery(); const handleCreateModalSubmit = async (data: { description: string; @@ -72,13 +71,7 @@ function Header(props: Props) { </div> <div className="sw-mb-4">{translate('permission_templates.page.description')}</div> </div> - {gitHubProvisioningStatus && ( - <span> - <FlagMessage variant="warning" className="sw-w-fit sw-mb-4"> - {translate('permission_templates.github_warning')} - </FlagMessage> - </span> - )} + <ProvisioningWarning /> {createModal && ( <Form diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/ProvisioningWarning.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/ProvisioningWarning.tsx new file mode 100644 index 00000000000..a286ab247e9 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/ProvisioningWarning.tsx @@ -0,0 +1,42 @@ +/* + * 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 { FlagMessage } from 'design-system'; +import React from 'react'; +import { translate, translateWithParameters } from '../../../helpers/l10n'; +import { useGithubProvisioningEnabledQuery } from '../../../queries/identity-provider/github'; +import { useGilabProvisioningEnabledQuery } from '../../../queries/identity-provider/gitlab'; +import { AlmKeys } from '../../../types/alm-settings'; + +export default function ProvisioningWarning() { + const { data: gitHubProvisioningStatus } = useGithubProvisioningEnabledQuery(); + const { data: gitLabProvisioningStatus } = useGilabProvisioningEnabledQuery(); + + if (gitHubProvisioningStatus || gitLabProvisioningStatus) { + return ( + <FlagMessage variant="warning" className="sw-w-fit sw-mb-4"> + {translateWithParameters( + 'permission_templates.provisioning_warning', + translate('alm', gitHubProvisioningStatus ? AlmKeys.GitHub : AlmKeys.GitLab), + )} + </FlagMessage> + ); + } + return null; +} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/Template.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/Template.tsx index c29169de24a..d4f96c2fa0c 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/Template.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/Template.tsx @@ -17,21 +17,20 @@ * 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, LargeCenteredLayout, PageContentFontWrapper } from 'design-system'; +import { LargeCenteredLayout, PageContentFontWrapper } from 'design-system'; import { without } from 'lodash'; import * as React from 'react'; import { Helmet } from 'react-helmet-async'; import * as api from '../../../api/permissions'; import AllHoldersList from '../../../components/permissions/AllHoldersList'; import { FilterOption } from '../../../components/permissions/SearchForm'; -import UseQuery from '../../../helpers/UseQuery'; import { translate } from '../../../helpers/l10n'; import { PERMISSIONS_ORDER_FOR_PROJECT_TEMPLATE, convertToPermissionDefinitions, } from '../../../helpers/permissions'; -import { useGithubProvisioningEnabledQuery } from '../../../queries/identity-provider/github'; import { Paging, PermissionGroup, PermissionTemplate, PermissionUser } from '../../../types/types'; +import ProvisioningWarning from './ProvisioningWarning'; import TemplateDetails from './TemplateDetails'; import TemplateHeader from './TemplateHeader'; @@ -348,15 +347,7 @@ export default class Template extends React.PureComponent<Props, State> { /> <main> <TemplateDetails template={template} /> - <UseQuery query={useGithubProvisioningEnabledQuery}> - {({ data: githubProvisioningStatus }) => - githubProvisioningStatus ? ( - <FlagMessage variant="warning" className="sw-w-fit sw-mb-4"> - {translate('permission_templates.github_warning')} - </FlagMessage> - ) : null - } - </UseQuery> + <ProvisioningWarning /> <AllHoldersList filter={filter} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/PermissionTemplatesApp-it.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/PermissionTemplatesApp-it.tsx index 77353140542..a7e00e7e97a 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/PermissionTemplatesApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/PermissionTemplatesApp-it.tsx @@ -29,6 +29,7 @@ import DopTranslationServiceMock from '../../../../api/mocks/DopTranslationServi import GithubProvisioningServiceMock from '../../../../api/mocks/GithubProvisioningServiceMock'; import GitlabProvisioningServiceMock from '../../../../api/mocks/GitlabProvisioningServiceMock'; import PermissionsServiceMock from '../../../../api/mocks/PermissionsServiceMock'; +import { mockGitlabConfiguration } from '../../../../helpers/mocks/alm-integrations'; import { mockGitHubConfiguration } from '../../../../helpers/mocks/dop-translation'; import { mockPermissionGroup, mockPermissionUser } from '../../../../helpers/mocks/permissions'; import { PERMISSIONS_ORDER_FOR_PROJECT_TEMPLATE } from '../../../../helpers/permissions'; @@ -419,6 +420,21 @@ it('should show github warning', async () => { expect(await ui.githubWarning.find()).toBeInTheDocument(); }); +it('should show gitlab warning', async () => { + const user = userEvent.setup(); + const ui = getPageObject(user); + + gitlabHandler.setGitlabConfigurations([ + mockGitlabConfiguration({ id: '1', enabled: true, provisioningType: ProvisioningType.auto }), + ]); + renderPermissionTemplatesApp(undefined, [Feature.GitlabProvisioning]); + + expect(await ui.gitlabWarning.find()).toBeInTheDocument(); + await ui.openTemplateDetails('Permission Template 1'); + + expect(await ui.gitlabWarning.find()).toBeInTheDocument(); +}); + function getPageObject(user: UserEvent) { const ui = { loading: byText('loading'), @@ -431,7 +447,8 @@ function getPageObject(user: UserEvent) { byRole('button', { name: `projects_role.${permission}` }), onlyUsersBtn: byRole('radio', { name: 'users.page' }), onlyGroupsBtn: byRole('radio', { name: 'user_groups.page' }), - githubWarning: byText('permission_templates.github_warning'), + githubWarning: byText('permission_templates.provisioning_warning.alm.github'), + gitlabWarning: byText('permission_templates.provisioning_warning.alm.gitlab'), showAllBtn: byRole('radio', { name: 'all' }), searchInput: byRole('searchbox', { name: 'search.search_for_users_or_groups' }), loadMoreBtn: byRole('button', { name: 'show_more' }), diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx index 347f3ae37de..c02ff14c8b9 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx @@ -31,9 +31,11 @@ import * as React from 'react'; import { bulkApplyTemplate, getPermissionTemplates } from '../../api/permissions'; import { Project } from '../../api/project-management'; import MandatoryFieldsExplanation from '../../components/ui/MandatoryFieldsExplanation'; +import UseQuery from '../../helpers/UseQuery'; import { toISO8601WithOffsetString } from '../../helpers/dates'; import { addGlobalErrorMessageFromAPI } from '../../helpers/globalMessages'; import { translate, translateWithParameters } from '../../helpers/l10n'; +import { useGithubProvisioningEnabledQuery } from '../../queries/identity-provider/github'; import { PermissionTemplate } from '../../types/types'; export interface Props { @@ -144,11 +146,16 @@ export default class BulkApplyTemplateModal extends React.PureComponent<Props, S if (isSelectionOnlyManaged) { return ( - <FlagMessage variant="error" className="sw-my-2"> - {translate( - 'permission_templates.bulk_apply_permission_template.apply_to_only_github_projects', + <UseQuery query={useGithubProvisioningEnabledQuery}> + {({ data: githubProvisioningStatus }) => ( + <FlagMessage variant="error" className="sw-my-2"> + {translateWithParameters( + 'permission_templates.bulk_apply_permission_template.apply_to_only_managed_projects', + githubProvisioningStatus ? translate('alm.github') : translate('alm.gitlab'), + )} + </FlagMessage> )} - </FlagMessage> + </UseQuery> ); } else if (isSelectionOnlyLocal) { return ( @@ -167,17 +174,22 @@ export default class BulkApplyTemplateModal extends React.PureComponent<Props, S } return ( - <FlagMessage variant="warning" className="sw-my-2"> - {translateWithParameters( - 'permission_templates.bulk_apply_permission_template.apply_to_selected', - localProjects.length, - )} - <br /> - {translateWithParameters( - 'permission_templates.bulk_apply_permission_template.apply_to_github_projects', - managedProjects.length, + <UseQuery query={useGithubProvisioningEnabledQuery}> + {({ data: githubProvisioningStatus }) => ( + <FlagMessage variant="warning" className="sw-my-2"> + {translateWithParameters( + 'permission_templates.bulk_apply_permission_template.apply_to_selected', + localProjects.length, + )} + <br /> + {translateWithParameters( + 'permission_templates.bulk_apply_permission_template.apply_to_managed_projects', + managedProjects.length, + githubProvisioningStatus ? translate('alm.github') : translate('alm.gitlab'), + )} + </FlagMessage> )} - </FlagMessage> + </UseQuery> ); }; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/ChangeDefaultVisibilityForm.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/ChangeDefaultVisibilityForm.tsx index 4846bd46fcb..46b70e18ab6 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/ChangeDefaultVisibilityForm.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/ChangeDefaultVisibilityForm.tsx @@ -23,6 +23,7 @@ import React, { useState } from 'react'; import { Visibility } from '~sonar-aligned/types/component'; import { translate } from '../../helpers/l10n'; import { useGithubProvisioningEnabledQuery } from '../../queries/identity-provider/github'; +import { useGilabProvisioningEnabledQuery } from '../../queries/identity-provider/gitlab'; export interface Props { defaultVisibility: Visibility; @@ -35,6 +36,14 @@ const FORM_ID = 'change-default-visibility-form'; export default function ChangeDefaultVisibilityForm(props: Readonly<Props>) { const [visibility, setVisibility] = useState(props.defaultVisibility); const { data: githubProbivisioningEnabled } = useGithubProvisioningEnabledQuery(); + const { data: gitlabProbivisioningEnabled } = useGilabProvisioningEnabledQuery(); + + let changeVisibilityTranslationKey = 'settings.projects.change_visibility_form.warning'; + if (githubProbivisioningEnabled) { + changeVisibilityTranslationKey += '.github'; + } else if (gitlabProbivisioningEnabled) { + changeVisibilityTranslationKey += '.gitlab'; + } const handleConfirmClick = (event: React.FormEvent<HTMLFormElement>) => { event.preventDefault(); @@ -62,13 +71,7 @@ export default function ChangeDefaultVisibilityForm(props: Readonly<Props>) { onChange={handleVisibilityChange} /> - <FlagMessage variant="warning"> - {translate( - `settings.projects.change_visibility_form.warning${ - githubProbivisioningEnabled ? '.github' : '' - }`, - )} - </FlagMessage> + <FlagMessage variant="warning">{translate(changeVisibilityTranslationKey)}</FlagMessage> </form> ); diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx index 8be3a04d3d5..0fdf60c537f 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx @@ -29,6 +29,7 @@ import DateFormatter from '../../components/intl/DateFormatter'; import { translate, translateWithParameters } from '../../helpers/l10n'; import { getComponentOverviewUrl } from '../../helpers/urls'; import { useGithubProvisioningEnabledQuery } from '../../queries/identity-provider/github'; +import { useGilabProvisioningEnabledQuery } from '../../queries/identity-provider/gitlab'; import { LoggedInUser } from '../../types/users'; import ProjectRowActions from './ProjectRowActions'; @@ -42,6 +43,7 @@ interface Props { export default function ProjectRow(props: Readonly<Props>) { const { currentUser, project, selected } = props; const { data: githubProvisioningEnabled } = useGithubProvisioningEnabledQuery(); + const { data: gitlabProbivisioningEnabled } = useGilabProvisioningEnabledQuery(); const handleProjectCheck = (checked: boolean) => { props.onProjectCheck(project, checked); @@ -66,7 +68,7 @@ export default function ProjectRow(props: Readonly<Props>) { </Tooltip> </LinkStandalone> {project.qualifier === ComponentQualifier.Project && - githubProvisioningEnabled && + (githubProvisioningEnabled || gitlabProbivisioningEnabled) && !project.managed && <Badge className="sw-ml-1">{translate('local')}</Badge>} </ContentCell> <ContentCell> diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx index a6710dcfa8a..b586d5a09a7 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx @@ -25,7 +25,6 @@ import { getComponentNavigation } from '../../api/navigation'; import { Project } from '../../api/project-management'; import { translate, translateWithParameters } from '../../helpers/l10n'; import { getComponentPermissionsUrl } from '../../helpers/urls'; -import { useGithubProvisioningEnabledQuery } from '../../queries/identity-provider/github'; import { LoggedInUser } from '../../types/users'; import ApplyTemplate from '../permissions/project/components/ApplyTemplate'; import RestoreAccessModal from './RestoreAccessModal'; @@ -40,7 +39,6 @@ export default function ProjectRowActions({ currentUser, project }: Props) { const [hasAccess, setHasAccess] = useState<boolean | undefined>(undefined); const [loading, setLoading] = useState(false); const [restoreAccessModal, setRestoreAccessModal] = useState(false); - const { data: githubProvisioningEnabled } = useGithubProvisioningEnabledQuery(); const fetchPermissions = async () => { setLoading(true); @@ -85,9 +83,7 @@ export default function ProjectRowActions({ currentUser, project }: Props) { {translate(project.managed ? 'show_permissions' : 'edit_permissions')} </ItemLink> )} - - {hasAccess === false && - (!project.managed || currentUser.local || !githubProvisioningEnabled) ? ( + {hasAccess === false && (!project.managed || currentUser.local) ? ( <ItemButton className="it__restore-access" onClick={() => setRestoreAccessModal(true)} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectManagementApp-it.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectManagementApp-it.tsx index 1d40db708ff..b2599e0a883 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectManagementApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectManagementApp-it.tsx @@ -24,9 +24,11 @@ import { byPlaceholderText, byRole, byText } from '~sonar-aligned/helpers/testSe import { ComponentQualifier } from '~sonar-aligned/types/component'; import DopTranslationServiceMock from '../../../api/mocks/DopTranslationServiceMock'; import GithubProvisioningServiceMock from '../../../api/mocks/GithubProvisioningServiceMock'; +import GitlabProvisioningServiceMock from '../../../api/mocks/GitlabProvisioningServiceMock'; import PermissionsServiceMock from '../../../api/mocks/PermissionsServiceMock'; import ProjectManagementServiceMock from '../../../api/mocks/ProjectsManagementServiceMock'; import SettingsServiceMock from '../../../api/mocks/SettingsServiceMock'; +import { mockGitlabConfiguration } from '../../../helpers/mocks/alm-integrations'; import { mockComponent } from '../../../helpers/mocks/component'; import { mockGitHubConfiguration } from '../../../helpers/mocks/dop-translation'; import { mockProject } from '../../../helpers/mocks/projects'; @@ -46,6 +48,7 @@ const permissionsHandler = new PermissionsServiceMock(); const settingsHandler = new SettingsServiceMock(); const dopTranslationHandler = new DopTranslationServiceMock(); const githubHandler = new GithubProvisioningServiceMock(dopTranslationHandler); +const gitlabHandler = new GitlabProvisioningServiceMock(); const handler = new ProjectManagementServiceMock(settingsHandler); jest.mock('../../../api/navigation', () => ({ @@ -182,6 +185,7 @@ afterEach(() => { settingsHandler.reset(); dopTranslationHandler.reset(); githubHandler.reset(); + gitlabHandler.reset(); handler.reset(); }); @@ -320,14 +324,17 @@ describe('Bulk permission templates', () => { ).toBeInTheDocument(); }); - it('should not be applied to managed projects', async () => { + it('should not be applied to managed GitHub projects', async () => { const user = userEvent.setup(); + dopTranslationHandler.gitHubConfigurations.push( + mockGitHubConfiguration({ provisioningType: ProvisioningType.auto }), + ); handler.setProjects( Array.from({ length: 11 }, (_, i) => mockProject({ key: i.toString(), name: `Test ${i}`, managed: true }), ), ); - renderProjectManagementApp(); + renderProjectManagementApp({}, {}, { featureList: [Feature.GithubProvisioning] }); expect(await ui.bulkApplyButton.find()).toBeDisabled(); const projects = ui.row.getAll().slice(1); @@ -339,13 +346,42 @@ describe('Bulk permission templates', () => { expect(await ui.bulkApplyDialog.find()).toBeInTheDocument(); expect( within(ui.bulkApplyDialog.get()).getByText( - 'permission_templates.bulk_apply_permission_template.apply_to_only_github_projects', + 'permission_templates.bulk_apply_permission_template.apply_to_only_managed_projects.alm.github', ), ).toBeInTheDocument(); expect(ui.bulkApplyDialog.by(ui.apply).get()).toBeDisabled(); }); - it('should not be applied to managed projects but to local project', async () => { + it('should not be applied to managed GitLab projects', async () => { + const user = userEvent.setup(); + handler.setProjects( + Array.from({ length: 11 }, (_, i) => + mockProject({ key: i.toString(), name: `Test ${i}`, managed: true }), + ), + ); + + gitlabHandler.setGitlabConfigurations([ + mockGitlabConfiguration({ id: '1', enabled: true, provisioningType: ProvisioningType.auto }), + ]); + renderProjectManagementApp({}, {}, { featureList: [Feature.GitlabProvisioning] }); + + expect(await ui.bulkApplyButton.find()).toBeDisabled(); + const projects = ui.row.getAll().slice(1); + expect(projects).toHaveLength(11); + await user.click(ui.checkAll.get()); + expect(ui.bulkApplyButton.get()).toBeEnabled(); + + await user.click(ui.bulkApplyButton.get()); + expect(await ui.bulkApplyDialog.find()).toBeInTheDocument(); + expect( + within(ui.bulkApplyDialog.get()).getByText( + 'permission_templates.bulk_apply_permission_template.apply_to_only_managed_projects.alm.gitlab', + ), + ).toBeInTheDocument(); + expect(ui.bulkApplyDialog.by(ui.apply).get()).toBeDisabled(); + }); + + it('should not be applied to managed GitLab projects but to local project', async () => { const user = userEvent.setup(); const allProjects = [ ...Array.from({ length: 6 }, (_, i) => @@ -357,7 +393,10 @@ describe('Bulk permission templates', () => { ]; handler.setProjects(allProjects); - renderProjectManagementApp(); + gitlabHandler.setGitlabConfigurations([ + mockGitlabConfiguration({ id: '1', enabled: true, provisioningType: ProvisioningType.auto }), + ]); + renderProjectManagementApp({}, {}, { featureList: [Feature.GitlabProvisioning] }); expect(await ui.bulkApplyButton.find()).toBeDisabled(); const projects = ui.row.getAll().slice(1); @@ -374,13 +413,51 @@ describe('Bulk permission templates', () => { ).toBeInTheDocument(); expect( within(ui.bulkApplyDialog.get()).getByText( - /permission_templates.bulk_apply_permission_template.apply_to_github_projects.6/, + /permission_templates.bulk_apply_permission_template.apply_to_managed_projects.6.alm.gitlab/, ), ).toBeInTheDocument(); expect(ui.bulkApplyDialog.by(ui.apply).get()).toBeEnabled(); }); }); +it('should not be applied to managed GitHub projects but to local project', async () => { + const user = userEvent.setup(); + const allProjects = [ + ...Array.from({ length: 6 }, (_, i) => + mockProject({ key: `${i.toString()} managed`, name: `Test managed ${i}`, managed: true }), + ), + ...Array.from({ length: 5 }, (_, i) => + mockProject({ key: `${i.toString()} local`, name: `Test local ${i}`, managed: false }), + ), + ]; + + handler.setProjects(allProjects); + dopTranslationHandler.gitHubConfigurations.push( + mockGitHubConfiguration({ provisioningType: ProvisioningType.auto }), + ); + renderProjectManagementApp({}, {}, { featureList: [Feature.GithubProvisioning] }); + + expect(await ui.bulkApplyButton.find()).toBeDisabled(); + const projects = ui.row.getAll().slice(1); + expect(projects).toHaveLength(11); + await user.click(ui.checkAll.get()); + expect(ui.bulkApplyButton.get()).toBeEnabled(); + + await user.click(ui.bulkApplyButton.get()); + expect(await ui.bulkApplyDialog.find()).toBeInTheDocument(); + expect( + within(ui.bulkApplyDialog.get()).getByText( + /permission_templates.bulk_apply_permission_template.apply_to_selected.5/, + ), + ).toBeInTheDocument(); + expect( + within(ui.bulkApplyDialog.get()).getByText( + /permission_templates.bulk_apply_permission_template.apply_to_managed_projects.6.alm.github/, + ), + ).toBeInTheDocument(); + expect(ui.bulkApplyDialog.by(ui.apply).get()).toBeEnabled(); +}); + it('should load more and change the filter without caching old pages', async () => { const user = userEvent.setup(); handler.setProjects([ |