+++ /dev/null
-/*
- * 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 { Spinner } from '@sonarsource/echoes-react';
-import {
- BasicSeparator,
- ButtonPrimary,
- FlagMessage,
- FormField,
- InputField,
- InputTextArea,
- SubHeading,
-} from 'design-system';
-import React, { ChangeEvent, FormEvent, useState } from 'react';
-import { useIntl } from 'react-intl';
-import { useCurrentLoginUser } from '../../../../app/components/current-user/CurrentUserContext';
-import MandatoryFieldsExplanation from '../../../../components/ui/MandatoryFieldsExplanation';
-import { translate, translateWithParameters } from '../../../../helpers/l10n';
-import { parseError } from '../../../../helpers/request';
-import { useSendTestEmailMutation } from '../../../../queries/emails';
-
-export default function EmailForm() {
- const currentUser = useCurrentLoginUser();
- const { formatMessage } = useIntl();
- const { isPending, isSuccess, mutate: sendTestEmail } = useSendTestEmailMutation();
-
- const [error, setError] = useState<string | undefined>();
- const [message, setMessage] = useState(
- formatMessage({ id: 'email_configuration.test.message_text' }),
- );
- const [recipient, setRecipient] = useState(currentUser.email ?? '');
- const [subject, setSubject] = useState(formatMessage({ id: 'email_configuration.test.subject' }));
-
- const handleFormSubmit = (event: FormEvent) => {
- event.preventDefault();
- sendTestEmail(
- { message, recipient, subject },
- {
- onError: async (error) => {
- const errorMessage = await parseError(error);
- setError(errorMessage);
- },
- },
- );
- };
-
- const onMessageChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
- setMessage(event.target.value);
- };
-
- const onRecipientChange = (event: ChangeEvent<HTMLInputElement>) => {
- setRecipient(event.target.value);
- };
-
- const onSubjectChange = (event: ChangeEvent<HTMLInputElement>) => {
- setSubject(event.target.value);
- };
-
- return (
- <>
- <BasicSeparator />
- <div className="sw-p-6 sw-flex sw-gap-12">
- <div className="sw-w-abs-300">
- <SubHeading>{translate('email_configuration.test.title')}</SubHeading>
- <div className="sw-mt-1">
- <MandatoryFieldsExplanation />
- </div>
- </div>
-
- <form className="sw-flex-1" onSubmit={handleFormSubmit}>
- {isSuccess && (
- <FlagMessage variant="success">
- {translateWithParameters('email_configuration.test.email_was_sent_to_x', recipient)}
- </FlagMessage>
- )}
-
- {error !== undefined && <FlagMessage variant="error">{error}</FlagMessage>}
-
- <FormField label={translate('email_configuration.test.to_address')} required>
- <InputField
- disabled={isPending}
- id="test-email-to"
- onChange={onRecipientChange}
- required
- size="large"
- type="email"
- value={recipient}
- />
- </FormField>
- <FormField label={translate('email_configuration.test.subject')}>
- <InputField
- disabled={isPending}
- id="test-email-subject"
- onChange={onSubjectChange}
- size="large"
- type="text"
- value={subject}
- />
- </FormField>
- <FormField label={translate('email_configuration.test.message')} required>
- <InputTextArea
- disabled={isPending}
- id="test-email-message"
- onChange={onMessageChange}
- required
- rows={5}
- size="large"
- value={message}
- />
- </FormField>
-
- <ButtonPrimary disabled={isPending} type="submit" className="sw-mt-2">
- {translate('email_configuration.test.send')}
- </ButtonPrimary>
- <Spinner isLoading={isPending} className="sw-ml-2" />
- </form>
- </div>
- </>
- );
-}
import { FormattedMessage } from 'react-intl';
import { translate } from '../../../../helpers/l10n';
import { AuthMethod, EmailConfiguration } from '../../../../types/system';
+import EmailTestModal from './EmailTestModal';
interface EmailTestModalProps {
emailConfiguration: EmailConfiguration;
return (
<>
+ <BasicSeparator className="sw-my-6" />
+ <EmailTestModal />
<BasicSeparator className="sw-my-6" />
<div className="sw-flex sw-justify-between">
<div className="sw-grid sw-gap-4">
--- /dev/null
+/*
+ * 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 { Button, ButtonVariety, Modal } from '@sonarsource/echoes-react';
+import {
+ addGlobalErrorMessage,
+ addGlobalSuccessMessage,
+ FormField,
+ InputField,
+ InputTextArea,
+} from 'design-system';
+import React, { FormEvent, useState } from 'react';
+import { useIntl } from 'react-intl';
+import { isEmail } from 'validator';
+import { useCurrentLoginUser } from '../../../../app/components/current-user/CurrentUserContext';
+import { translate } from '../../../../helpers/l10n';
+import { useSendTestEmailMutation } from '../../../../queries/emails';
+
+const FORM_ID = 'test-email-form';
+
+export default function EmailTestModal() {
+ const [isOpen, setIsOpen] = useState(false);
+ const currentUser = useCurrentLoginUser();
+ const { formatMessage } = useIntl();
+ const { isPending, mutate: sendTestEmail } = useSendTestEmailMutation();
+
+ const [message, setMessage] = useState(
+ formatMessage({ id: 'email_notification.test.message_text' }),
+ );
+ const [recipient, setRecipient] = useState(currentUser.email ?? '');
+ const [subject, setSubject] = useState(formatMessage({ id: 'email_notification.test.subject' }));
+
+ const handleFormSubmit = (event: FormEvent) => {
+ event.preventDefault();
+ sendTestEmail(
+ { message, recipient, subject },
+ {
+ onError: () => {
+ addGlobalErrorMessage(translate('email_notification.test.failure'));
+ },
+ onSuccess: () => {
+ addGlobalSuccessMessage(translate('email_notification.test.success'));
+ },
+ onSettled: () => setIsOpen(false),
+ },
+ );
+ };
+
+ const body = (
+ <form className="sw-flex-1" id={FORM_ID} onSubmit={handleFormSubmit}>
+ <FormField
+ htmlFor="test-email-to"
+ label={formatMessage({ id: 'email_notification.test.to_address' })}
+ required
+ >
+ <InputField
+ disabled={isPending}
+ id="test-email-to"
+ onChange={(event) => setRecipient(event.target.value)}
+ required
+ size="full"
+ type="email"
+ value={recipient}
+ />
+ </FormField>
+ <FormField
+ htmlFor="test-email-subject"
+ label={formatMessage({ id: 'email_notification.test.subject' })}
+ >
+ <InputField
+ disabled={isPending}
+ id="test-email-subject"
+ onChange={(event) => setSubject(event.target.value)}
+ size="full"
+ type="text"
+ value={subject}
+ />
+ </FormField>
+ <FormField
+ htmlFor="test-email-message"
+ label={formatMessage({ id: 'email_notification.test.message' })}
+ required
+ >
+ <InputTextArea
+ disabled={isPending}
+ id="test-email-message"
+ onChange={(event) => setMessage(event.target.value)}
+ required
+ rows={5}
+ size="full"
+ value={message}
+ />
+ </FormField>
+ </form>
+ );
+
+ return (
+ <Modal
+ content={body}
+ isOpen={isOpen}
+ onOpenChange={setIsOpen}
+ primaryButton={
+ <Button
+ isDisabled={!isEmail(recipient) || isPending}
+ form={FORM_ID}
+ variety={ButtonVariety.Primary}
+ type="submit"
+ >
+ {formatMessage({ id: 'email_notification.test.submit' })}
+ </Button>
+ }
+ secondaryButton={
+ <Button onClick={() => setIsOpen(false)} variety={ButtonVariety.Default}>
+ {formatMessage({ id: 'cancel' })}
+ </Button>
+ }
+ title={formatMessage({ id: 'email_notification.test.modal_title' })}
+ >
+ <div className="sw-flex sw-justify-between sw-items-center">
+ <span className="sw-body-md-highlight">
+ {formatMessage({ id: 'email_notification.test.title' })}
+ </span>
+ <Button onClick={() => setIsOpen(true)} variety={ButtonVariety.Default}>
+ {formatMessage({ id: 'email_notification.test.create_test_email' })}
+ </Button>
+ </div>
+ </Modal>
+ );
+}
import React from 'react';
import { byLabelText, byRole, byTestId, byText } from '~sonar-aligned/helpers/testSelector';
import SystemServiceMock from '../../../../../api/mocks/SystemServiceMock';
+import * as settingsApi from '../../../../../api/settings';
import * as api from '../../../../../api/system';
+import { CurrentUserContext } from '../../../../../app/components/current-user/CurrentUserContext';
import { mockEmailConfiguration } from '../../../../../helpers/mocks/system';
+import { mockCurrentUser } from '../../../../../helpers/testMocks';
import { renderComponent } from '../../../../../helpers/testReactTestingUtils';
+import { Permissions } from '../../../../../types/permissions';
import { AuthMethod } from '../../../../../types/system';
import EmailNotification from '../EmailNotification';
edit: byRole('button', {
name: 'edit',
}),
+
+ // test modal
+ test_email: byRole('button', { name: 'email_notification.test.create_test_email' }),
+ test_email_title: byRole('heading', { name: 'email_notification.test.modal_title' }),
+ test_email_to_address: byRole('textbox', {
+ name: 'email_notification.test.to_address required',
+ }),
+ test_email_subject: byRole('textbox', { name: 'email_notification.test.subject' }),
+ test_email_message: byRole('textbox', { name: 'email_notification.test.message required' }),
+ test_email_submit: byRole('button', { name: 'email_notification.test.submit' }),
};
describe('Email Basic Configuration', () => {
});
});
+describe('EmailNotification send test email', () => {
+ it('should render the EmailTestModal', async () => {
+ const user = userEvent.setup();
+ systemHandler.addEmailConfiguration(
+ mockEmailConfiguration(AuthMethod.Basic, { id: 'email-1' }),
+ );
+
+ renderEmailNotifications();
+ expect(await ui.overviewHeading.find()).toBeInTheDocument();
+
+ await user.click(ui.test_email.get());
+
+ expect(ui.test_email_title.get()).toBeVisible();
+ expect(ui.test_email_to_address.get()).toBeVisible();
+ expect(ui.test_email_to_address.get()).toHaveValue('');
+ expect(ui.test_email_subject.get()).toBeVisible();
+ expect(ui.test_email_subject.get()).toHaveValue('email_notification.test.subject');
+ expect(ui.test_email_message.get()).toBeVisible();
+ expect(ui.test_email_message.get()).toHaveValue('email_notification.test.message_text');
+ });
+
+ it('should be possible to send a test email', async () => {
+ jest.spyOn(settingsApi, 'sendTestEmail');
+ const user = userEvent.setup();
+ systemHandler.addEmailConfiguration(
+ mockEmailConfiguration(AuthMethod.Basic, { id: 'email-1' }),
+ );
+
+ renderEmailNotifications();
+ expect(await ui.overviewHeading.find()).toBeInTheDocument();
+
+ await user.click(ui.test_email.get());
+
+ expect(ui.test_email_submit.get()).toBeDisabled();
+ await user.type(ui.test_email_to_address.get(), 'test@test.com');
+ expect(ui.test_email_submit.get()).toBeEnabled();
+
+ await user.clear(ui.test_email_subject.get());
+ await user.type(ui.test_email_subject.get(), 'Test subject');
+ await user.clear(ui.test_email_message.get());
+ await user.type(ui.test_email_message.get(), 'This is a test message');
+
+ await user.click(ui.test_email_submit.get());
+ expect(settingsApi.sendTestEmail).toHaveBeenCalledTimes(1);
+ expect(settingsApi.sendTestEmail).toHaveBeenCalledWith(
+ 'test@test.com',
+ 'Test subject',
+ 'This is a test message',
+ );
+ });
+});
+
function renderEmailNotifications() {
- return renderComponent(<EmailNotification />);
+ return renderComponent(
+ <CurrentUserContext.Provider
+ value={{
+ currentUser: mockCurrentUser({
+ isLoggedIn: true,
+ permissions: { global: [Permissions.Admin] },
+ }),
+ updateCurrentUserHomepage: () => {},
+ updateDismissedNotices: () => {},
+ }}
+ >
+ <EmailNotification />
+ </CurrentUserContext.Provider>,
+ );
}
},
unknown
>({
- mutationFn: ({ message, recipient, subject }) => sendTestEmail(message, recipient, subject),
+ mutationFn: ({ message, recipient, subject }) => sendTestEmail(recipient, subject, message),
});
}
email_notification.form.basic_password.description=Password used to authenticate to the SMTP server.
email_notification.form.oauth_auth.title=Modern Authentication
email_notification.form.oauth_auth.description=Authenticate with OAuth
-email_notification.form.oauth_auth.supported=Supported: Microsoft
+email_notification.form.oauth_auth.supported=Supported providers: Microsoft
email_notification.form.oauth_auth.recommended_reason=for stronger security compliance
email_notification.form.oauth_authentication_host=Authentication host
email_notification.form.oauth_authentication_host.description=Host of the Identity Provider issuing access tokens.
email_notification.overview.private=Hidden for security reasons
email_notification.form.private=**************
email_notification.overview.value={0} value
-
-email_configuration.test.title=Test Configuration
-email_configuration.test.to_address=To
-email_configuration.test.subject=Subject
-email_configuration.test.message=Message
-email_configuration.test.message_text=This is a test message from SonarQube.
-email_configuration.test.send=Send Test Email
-email_configuration.test.email_was_sent_to_x=Email was sent to {0}
-
+email_notification.test.title=Test configuration
+email_notification.test.modal_title=Test email
+email_notification.test.to_address=To
+email_notification.test.subject=Subject
+email_notification.test.message=Message
+email_notification.test.message_text=This is a test message from SonarQube.
+email_notification.test.create_test_email=Create test email
+email_notification.test.submit=Send test email
+email_notification.test.success=Your email was sent successfully
+email_notification.test.failure=Your email could not be sent. Ensure your authentication configuration settings and email recipient are valid.
+email_notification.state.value_should_be_valid_email=A valid email address is required.
#------------------------------------------------------------------------------
#