diff options
author | Philippe Perrin <philippe.perrin@sonarsource.com> | 2022-10-26 11:12:22 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2022-10-26 20:03:10 +0000 |
commit | 47012e64f35a8b63575e40efed7981a37979945a (patch) | |
tree | 2b51c0450b1aa58ffe4f946e1fe6585afb9437d4 /server/sonar-web/src/main/js | |
parent | dc592db7bb160b5c43fae1fcfd7e222ed0c875a1 (diff) | |
download | sonarqube-47012e64f35a8b63575e40efed7981a37979945a.tar.gz sonarqube-47012e64f35a8b63575e40efed7981a37979945a.zip |
SONAR-17515 Add an info box on the authentication settings page to advertise about the login message feature
Diffstat (limited to 'server/sonar-web/src/main/js')
3 files changed, 154 insertions, 99 deletions
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AdditionalCategories-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AdditionalCategories-test.tsx.snap index 190caae8682..4d5762daebd 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AdditionalCategories-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AdditionalCategories-test.tsx.snap @@ -120,7 +120,7 @@ exports[`should render additional categories component correctly 5`] = ` `; exports[`should render additional categories component correctly 6`] = ` -<Authentication +<withAvailableFeaturesContext(Authentication) categories={Array []} component={ Object { diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx b/server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx index 4653c2a69fb..316c8099252 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx @@ -20,7 +20,11 @@ import * as React from 'react'; import { FormattedMessage } from 'react-intl'; import { useSearchParams } from 'react-router-dom'; +import withAvailableFeatures, { + WithAvailableFeaturesProps +} from '../../../../app/components/available-features/withAvailableFeatures'; import DocLink from '../../../../components/common/DocLink'; +import Link from '../../../../components/common/Link'; import ScreenPositionHelper from '../../../../components/common/ScreenPositionHelper'; import BoxedTabs, { getTabId, getTabPanelId } from '../../../../components/controls/BoxedTabs'; import { Alert } from '../../../../components/ui/Alert'; @@ -28,6 +32,7 @@ import { translate } from '../../../../helpers/l10n'; import { getBaseUrl } from '../../../../helpers/system'; import { searchParamsToQuery } from '../../../../helpers/urls'; import { AlmKeys } from '../../../../types/alm-settings'; +import { Feature } from '../../../../types/features'; import { ExtendedSettingDefinition } from '../../../../types/settings'; import { AUTHENTICATION_CATEGORY } from '../../constants'; import CategoryDefinitionsList from '../CategoryDefinitionsList'; @@ -65,7 +70,7 @@ function renderDevOpsIcon(key: string) { ); } -export default function Authentication(props: Props) { +export function Authentication(props: Props & WithAvailableFeaturesProps) { const { definitions } = props; const [query, setSearchParams] = useSearchParams(); @@ -112,7 +117,23 @@ export default function Authentication(props: Props) { <h1 className="page-title">{translate('settings.authentication.title')}</h1> </header> - <div className="spacer-top huge-spacer-bottom"> + {props.hasFeature(Feature.LoginMessage) && ( + <Alert variant="info"> + <FormattedMessage + id="settings.authentication.custom_message_information" + defaultMessage={translate('settings.authentication.custom_message_information')} + values={{ + link: ( + <Link to="/admin/settings?category=general#sonar.login.message"> + {translate('settings.authentication.custom_message_information.link')} + </Link> + ) + }} + /> + </Alert> + )} + + <div className="big-spacer-top huge-spacer-bottom"> <p>{translate('settings.authentication.description')}</p> </div> @@ -171,3 +192,5 @@ export default function Authentication(props: Props) { </> ); } + +export default withAvailableFeatures(Authentication); diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/__tests__/Authentication-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/authentication/__tests__/Authentication-test.tsx index 7027cbef4e4..0671022d940 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/authentication/__tests__/Authentication-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/authentication/__tests__/Authentication-test.tsx @@ -21,8 +21,10 @@ import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import AuthenticationServiceMock from '../../../../../api/mocks/AuthenticationServiceMock'; +import { AvailableFeaturesContext } from '../../../../../app/components/available-features/AvailableFeaturesContext'; import { mockDefinition } from '../../../../../helpers/mocks/settings'; import { renderComponent } from '../../../../../helpers/testReactTestingUtils'; +import { Feature } from '../../../../../types/features'; import { ExtendedSettingDefinition, SettingType } from '../../../../../types/settings'; import Authentication from '../Authentication'; @@ -86,103 +88,133 @@ it('should render tabs and allow navigation', async () => { ); }); -it('should allow user to test the configuration', async () => { - const user = userEvent.setup(); - - const definitions = [ - mockDefinition({ - key: 'sonar.auth.saml.certificate.secured', - category: 'authentication', - subCategory: 'saml', - name: 'Certificate', - description: 'Secured certificate', - type: SettingType.PASSWORD - }), - mockDefinition({ - key: 'sonar.auth.saml.enabled', - category: 'authentication', - subCategory: 'saml', - name: 'Enabled', - description: 'To enable the flag', - type: SettingType.BOOLEAN - }) - ]; - - renderAuthentication(definitions); - - await user.click(await screen.findByText('settings.almintegration.form.secret.update_field')); - - await user.click(screen.getByRole('textbox', { name: 'Certificate' })); - await user.keyboard('new certificate'); - - expect(screen.getByText('settings.authentication.saml.form.test')).toHaveClass('disabled'); - - await user.click(screen.getByRole('button', { name: 'settings.authentication.saml.form.save' })); - - expect(screen.getByText('settings.authentication.saml.form.test')).not.toHaveClass('disabled'); +describe('SAML tab', () => { + it('should allow user to test the configuration', async () => { + const user = userEvent.setup(); + + const definitions = [ + mockDefinition({ + key: 'sonar.auth.saml.certificate.secured', + category: 'authentication', + subCategory: 'saml', + name: 'Certificate', + description: 'Secured certificate', + type: SettingType.PASSWORD + }), + mockDefinition({ + key: 'sonar.auth.saml.enabled', + category: 'authentication', + subCategory: 'saml', + name: 'Enabled', + description: 'To enable the flag', + type: SettingType.BOOLEAN + }) + ]; + + renderAuthentication(definitions); + + await user.click(await screen.findByText('settings.almintegration.form.secret.update_field')); + + await user.click(screen.getByRole('textbox', { name: 'Certificate' })); + await user.keyboard('new certificate'); + + expect(screen.getByText('settings.authentication.saml.form.test')).toHaveClass('disabled'); + + await user.click( + screen.getByRole('button', { name: 'settings.authentication.saml.form.save' }) + ); + + expect(screen.getByText('settings.authentication.saml.form.test')).not.toHaveClass('disabled'); + }); + + it('should allow user to edit fields and save configuration', async () => { + const user = userEvent.setup(); + const definitions = mockDefinitionFields; + renderAuthentication(definitions); + + expect(screen.getByRole('button', { name: 'off' })).toHaveAttribute('aria-disabled', 'true'); + // update fields + await user.click(screen.getByRole('textbox', { name: 'test1' })); + await user.keyboard('new test1'); + + await user.click(screen.getByRole('textbox', { name: 'test2' })); + await user.keyboard('new test2'); + // check if enable is allowed after updating + expect(screen.getByRole('button', { name: 'off' })).toHaveAttribute('aria-disabled', 'false'); + + // reset value + await user.click(screen.getByRole('textbox', { name: 'test2' })); + await user.keyboard('{Control>}a{/Control}{Backspace}'); + await user.click( + screen.getByRole('button', { name: 'settings.authentication.saml.form.save' }) + ); + expect(screen.getByRole('button', { name: 'off' })).toHaveAttribute('aria-disabled', 'true'); + + await user.click(screen.getByRole('textbox', { name: 'test2' })); + await user.keyboard('new test2'); + expect(screen.getByRole('button', { name: 'off' })).toHaveAttribute('aria-disabled', 'false'); + + expect( + screen.getByRole('button', { name: 'settings.almintegration.form.secret.update_field' }) + ).toBeInTheDocument(); + await user.click( + screen.getByRole('button', { name: 'settings.almintegration.form.secret.update_field' }) + ); + // check for secure fields + expect(screen.getByRole('textbox', { name: 'Certificate' })).toBeInTheDocument(); + await user.click(screen.getByRole('textbox', { name: 'Certificate' })); + await user.keyboard('new certificate'); + // enable the configuration + await user.click(screen.getByRole('button', { name: 'off' })); + expect(screen.getByRole('button', { name: 'on' })).toBeInTheDocument(); + + await user.click( + screen.getByRole('button', { name: 'settings.authentication.saml.form.save' }) + ); + expect(screen.getByText('settings.authentication.saml.form.save_success')).toBeInTheDocument(); + // check after switching tab that the flag is still enabled + await user.click(screen.getByRole('tab', { name: 'github GitHub' })); + await user.click(screen.getByRole('tab', { name: 'SAML' })); + + expect(screen.getByRole('button', { name: 'on' })).toBeInTheDocument(); + }); + + it('should handle and show errors to the user', async () => { + const user = userEvent.setup(); + const definitions = mockDefinitionFields; + renderAuthentication(definitions); + + await user.click(screen.getByRole('textbox', { name: 'test1' })); + await user.keyboard('value'); + await user.click(screen.getByRole('textbox', { name: 'test2' })); + await user.keyboard('{Control>}a{/Control}error'); + await user.click( + screen.getByRole('button', { name: 'settings.authentication.saml.form.save' }) + ); + expect(screen.getByText('settings.authentication.saml.form.save_partial')).toBeInTheDocument(); + }); + + it('should not display the login message feature info box', () => { + renderAuthentication([]); + + expect( + screen.queryByText('settings.authentication.custom_message_information') + ).not.toBeInTheDocument(); + }); + + it('should display the login message feature info box', () => { + renderAuthentication([], [Feature.LoginMessage]); + + expect( + screen.getByText('settings.authentication.custom_message_information') + ).toBeInTheDocument(); + }); }); -it('should allow user to edit fields and save configuration', async () => { - const user = userEvent.setup(); - const definitions = mockDefinitionFields; - renderAuthentication(definitions); - - expect(screen.getByRole('button', { name: 'off' })).toHaveAttribute('aria-disabled', 'true'); - // update fields - await user.click(screen.getByRole('textbox', { name: 'test1' })); - await user.keyboard('new test1'); - - await user.click(screen.getByRole('textbox', { name: 'test2' })); - await user.keyboard('new test2'); - // check if enable is allowed after updating - expect(screen.getByRole('button', { name: 'off' })).toHaveAttribute('aria-disabled', 'false'); - - // reset value - await user.click(screen.getByRole('textbox', { name: 'test2' })); - await user.keyboard('{Control>}a{/Control}{Backspace}'); - await user.click(screen.getByRole('button', { name: 'settings.authentication.saml.form.save' })); - expect(screen.getByRole('button', { name: 'off' })).toHaveAttribute('aria-disabled', 'true'); - - await user.click(screen.getByRole('textbox', { name: 'test2' })); - await user.keyboard('new test2'); - expect(screen.getByRole('button', { name: 'off' })).toHaveAttribute('aria-disabled', 'false'); - - expect( - screen.getByRole('button', { name: 'settings.almintegration.form.secret.update_field' }) - ).toBeInTheDocument(); - await user.click( - screen.getByRole('button', { name: 'settings.almintegration.form.secret.update_field' }) +function renderAuthentication(definitions: ExtendedSettingDefinition[], features: Feature[] = []) { + renderComponent( + <AvailableFeaturesContext.Provider value={features}> + <Authentication definitions={definitions} /> + </AvailableFeaturesContext.Provider> ); - // check for secure fields - expect(screen.getByRole('textbox', { name: 'Certificate' })).toBeInTheDocument(); - await user.click(screen.getByRole('textbox', { name: 'Certificate' })); - await user.keyboard('new certificate'); - // enable the configuration - await user.click(screen.getByRole('button', { name: 'off' })); - expect(screen.getByRole('button', { name: 'on' })).toBeInTheDocument(); - - await user.click(screen.getByRole('button', { name: 'settings.authentication.saml.form.save' })); - expect(screen.getByText('settings.authentication.saml.form.save_success')).toBeInTheDocument(); - // check after switching tab that the flag is still enabled - await user.click(screen.getByRole('tab', { name: 'github GitHub' })); - await user.click(screen.getByRole('tab', { name: 'SAML' })); - - expect(screen.getByRole('button', { name: 'on' })).toBeInTheDocument(); -}); - -it('should handle and show error to the user', async () => { - const user = userEvent.setup(); - const definitions = mockDefinitionFields; - renderAuthentication(definitions); - - await user.click(screen.getByRole('textbox', { name: 'test1' })); - await user.keyboard('value'); - await user.click(screen.getByRole('textbox', { name: 'test2' })); - await user.keyboard('{Control>}a{/Control}error'); - await user.click(screen.getByRole('button', { name: 'settings.authentication.saml.form.save' })); - expect(screen.getByText('settings.authentication.saml.form.save_partial')).toBeInTheDocument(); -}); - -function renderAuthentication(definitions: ExtendedSettingDefinition[]) { - renderComponent(<Authentication definitions={definitions} />); } |