]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-13984 Do not use email address from controlled input
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Mon, 30 Nov 2020 14:02:02 +0000 (15:02 +0100)
committersonartech <sonartech@sonarsource.com>
Thu, 3 Dec 2020 20:06:38 +0000 (20:06 +0000)
server/sonar-web/src/main/js/apps/settings/components/EmailForm.tsx
server/sonar-web/src/main/js/apps/settings/components/__tests__/EmailForm-test.tsx
server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/EmailForm-test.tsx.snap

index ad34b04c5b2b8b4882ff60d7c37fb6e288c0dff4..90fe52ce51104d3115aa8fec5f741252246675c6 100644 (file)
@@ -35,7 +35,7 @@ interface State {
   subject: string;
   message: string;
   loading: boolean;
-  success: boolean;
+  success?: string;
   error?: string;
 }
 
@@ -48,8 +48,7 @@ export class EmailForm extends React.PureComponent<Props, State> {
       recipient: this.props.currentUser.email || '',
       subject: translate('email_configuration.test.subject'),
       message: translate('email_configuration.test.message_text'),
-      loading: false,
-      success: false
+      loading: false
     };
   }
 
@@ -71,11 +70,11 @@ export class EmailForm extends React.PureComponent<Props, State> {
 
   handleFormSubmit = (event: React.FormEvent) => {
     event.preventDefault();
-    this.setState({ success: false, error: undefined, loading: true });
+    this.setState({ success: undefined, error: undefined, loading: true });
     const { recipient, subject, message } = this.state;
     sendTestEmail(recipient, subject, message).then(() => {
       if (this.mounted) {
-        this.setState({ success: true, loading: false });
+        this.setState({ success: recipient, loading: false });
       }
     }, this.handleError);
   };
@@ -93,6 +92,7 @@ export class EmailForm extends React.PureComponent<Props, State> {
   };
 
   render() {
+    const { error, loading, message, recipient, subject, success } = this.state;
     return (
       <div className="settings-definition">
         <div className="settings-definition-left">
@@ -102,20 +102,17 @@ export class EmailForm extends React.PureComponent<Props, State> {
         </div>
 
         <form className="settings-definition-right" onSubmit={this.handleFormSubmit}>
-          {this.state.success && (
+          {success && (
             <div className="form-field">
               <Alert variant="success">
-                {translateWithParameters(
-                  'email_configuration.test.email_was_sent_to_x',
-                  this.state.recipient
-                )}
+                {translateWithParameters('email_configuration.test.email_was_sent_to_x', success)}
               </Alert>
             </div>
           )}
 
-          {this.state.error != null && (
+          {error !== undefined && (
             <div className="form-field">
-              <Alert variant="error">{this.state.error}</Alert>
+              <Alert variant="error">{error}</Alert>
             </div>
           )}
 
@@ -126,12 +123,12 @@ export class EmailForm extends React.PureComponent<Props, State> {
             </label>
             <input
               className="settings-large-input"
-              disabled={this.state.loading}
+              disabled={loading}
               id="test-email-to"
               onChange={this.onRecipientChange}
               required={true}
               type="email"
-              value={this.state.recipient}
+              value={recipient}
             />
           </div>
           <div className="form-field">
@@ -140,11 +137,11 @@ export class EmailForm extends React.PureComponent<Props, State> {
             </label>
             <input
               className="settings-large-input"
-              disabled={this.state.loading}
+              disabled={loading}
               id="test-email-subject"
               onChange={this.onSubjectChange}
               type="text"
-              value={this.state.subject}
+              value={subject}
             />
           </div>
           <div className="form-field">
@@ -154,19 +151,19 @@ export class EmailForm extends React.PureComponent<Props, State> {
             </label>
             <textarea
               className="settings-large-input"
-              disabled={this.state.loading}
+              disabled={loading}
               id="test-email-message"
               onChange={this.onMessageChange}
               required={true}
               rows={5}
-              value={this.state.message}
+              value={message}
             />
           </div>
 
-          <SubmitButton disabled={this.state.loading}>
+          <SubmitButton disabled={loading}>
             {translate('email_configuration.test.send')}
           </SubmitButton>
-          {this.state.loading && <DeferredSpinner className="spacer-left" />}
+          {loading && <DeferredSpinner className="spacer-left" />}
         </form>
       </div>
     );
index e98ad8f87071f8d25b6d889f195580604b0fb224..b5e7b2e10f6fd4e5924a41508ad3450fbfdd4bf6 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+/* eslint-disable sonarjs/no-duplicate-string */
 import { shallow } from 'enzyme';
 import * as React from 'react';
+import { change, submit, waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
+import { sendTestEmail } from '../../../../api/settings';
 import { mockLoggedInUser } from '../../../../helpers/testMocks';
 import { EmailForm } from '../EmailForm';
 
+jest.mock('sonar-ui-common/helpers/request', () => ({
+  parseError: jest.fn().mockResolvedValue('Error message')
+}));
+
+jest.mock('../../../../api/settings', () => ({
+  sendTestEmail: jest.fn().mockResolvedValue(null)
+}));
+
 it('should render correctly', () => {
-  expect(shallow(<EmailForm currentUser={mockLoggedInUser()} />)).toMatchSnapshot();
+  const wrapper = shallowRender();
+  expect(wrapper).toMatchSnapshot('default');
+  wrapper.setState({ loading: true });
+  expect(wrapper).toMatchSnapshot('sending');
+  wrapper.setState({ loading: false, success: 'email@example.com' });
+  expect(wrapper).toMatchSnapshot('success');
+  wrapper.setState({ success: undefined, error: 'Some error message' });
+  expect(wrapper).toMatchSnapshot('error');
 });
+
+it('should correctly control the inputs', () => {
+  const wrapper = shallowRender();
+
+  change(wrapper.find('#test-email-to'), 'new@recipient.com');
+  expect(wrapper.state().recipient).toBe('new@recipient.com');
+
+  change(wrapper.find('#test-email-subject'), 'New subject');
+  expect(wrapper.state().subject).toBe('New subject');
+
+  change(wrapper.find('#test-email-message'), 'New message');
+  expect(wrapper.state().message).toBe('New message');
+});
+
+it('should correctly test the email sending', async () => {
+  const wrapper = shallowRender();
+
+  submit(wrapper.find('form'));
+  expect(sendTestEmail).toHaveBeenCalledWith(
+    'luke@skywalker.biz',
+    'email_configuration.test.subject',
+    'email_configuration.test.message_text'
+  );
+  expect(wrapper.state().loading).toBe(true);
+
+  await waitAndUpdate(wrapper);
+
+  expect(wrapper.state().loading).toBe(false);
+  expect(wrapper.state().error).toBeUndefined();
+  expect(wrapper.state().success).toBe('luke@skywalker.biz');
+
+  (sendTestEmail as jest.Mock).mockRejectedValueOnce(null);
+
+  submit(wrapper.find('form'));
+
+  await waitAndUpdate(wrapper);
+
+  expect(wrapper.state().success).toBeUndefined();
+  expect(wrapper.state().error).toBe('Error message');
+});
+
+function shallowRender(props: Partial<EmailForm['props']> = {}) {
+  return shallow<EmailForm>(
+    <EmailForm currentUser={mockLoggedInUser({ email: 'luke@skywalker.biz' })} {...props} />
+  );
+}
index cc15cb3104dc9c0652aec176fa7a47bba4a6c108..96b6595eb05a4e6357ceeae2946632acaa3aa348 100644 (file)
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`should render correctly 1`] = `
+exports[`should render correctly: default 1`] = `
 <div
   className="settings-definition"
 >
@@ -37,7 +37,295 @@ exports[`should render correctly 1`] = `
         onChange={[Function]}
         required={true}
         type="email"
-        value=""
+        value="luke@skywalker.biz"
+      />
+    </div>
+    <div
+      className="form-field"
+    >
+      <label
+        htmlFor="test-email-subject"
+      >
+        email_configuration.test.subject
+      </label>
+      <input
+        className="settings-large-input"
+        disabled={false}
+        id="test-email-subject"
+        onChange={[Function]}
+        type="text"
+        value="email_configuration.test.subject"
+      />
+    </div>
+    <div
+      className="form-field"
+    >
+      <label
+        htmlFor="test-email-message"
+      >
+        email_configuration.test.message
+        <em
+          className="mandatory"
+        >
+          *
+        </em>
+      </label>
+      <textarea
+        className="settings-large-input"
+        disabled={false}
+        id="test-email-message"
+        onChange={[Function]}
+        required={true}
+        rows={5}
+        value="email_configuration.test.message_text"
+      />
+    </div>
+    <SubmitButton
+      disabled={false}
+    >
+      email_configuration.test.send
+    </SubmitButton>
+  </form>
+</div>
+`;
+
+exports[`should render correctly: error 1`] = `
+<div
+  className="settings-definition"
+>
+  <div
+    className="settings-definition-left"
+  >
+    <h3
+      className="settings-definition-name"
+    >
+      email_configuration.test.title
+    </h3>
+  </div>
+  <form
+    className="settings-definition-right"
+    onSubmit={[Function]}
+  >
+    <div
+      className="form-field"
+    >
+      <Alert
+        variant="error"
+      >
+        Some error message
+      </Alert>
+    </div>
+    <div
+      className="form-field"
+    >
+      <label
+        htmlFor="test-email-to"
+      >
+        email_configuration.test.to_address
+        <em
+          className="mandatory"
+        >
+          *
+        </em>
+      </label>
+      <input
+        className="settings-large-input"
+        disabled={false}
+        id="test-email-to"
+        onChange={[Function]}
+        required={true}
+        type="email"
+        value="luke@skywalker.biz"
+      />
+    </div>
+    <div
+      className="form-field"
+    >
+      <label
+        htmlFor="test-email-subject"
+      >
+        email_configuration.test.subject
+      </label>
+      <input
+        className="settings-large-input"
+        disabled={false}
+        id="test-email-subject"
+        onChange={[Function]}
+        type="text"
+        value="email_configuration.test.subject"
+      />
+    </div>
+    <div
+      className="form-field"
+    >
+      <label
+        htmlFor="test-email-message"
+      >
+        email_configuration.test.message
+        <em
+          className="mandatory"
+        >
+          *
+        </em>
+      </label>
+      <textarea
+        className="settings-large-input"
+        disabled={false}
+        id="test-email-message"
+        onChange={[Function]}
+        required={true}
+        rows={5}
+        value="email_configuration.test.message_text"
+      />
+    </div>
+    <SubmitButton
+      disabled={false}
+    >
+      email_configuration.test.send
+    </SubmitButton>
+  </form>
+</div>
+`;
+
+exports[`should render correctly: sending 1`] = `
+<div
+  className="settings-definition"
+>
+  <div
+    className="settings-definition-left"
+  >
+    <h3
+      className="settings-definition-name"
+    >
+      email_configuration.test.title
+    </h3>
+  </div>
+  <form
+    className="settings-definition-right"
+    onSubmit={[Function]}
+  >
+    <div
+      className="form-field"
+    >
+      <label
+        htmlFor="test-email-to"
+      >
+        email_configuration.test.to_address
+        <em
+          className="mandatory"
+        >
+          *
+        </em>
+      </label>
+      <input
+        className="settings-large-input"
+        disabled={true}
+        id="test-email-to"
+        onChange={[Function]}
+        required={true}
+        type="email"
+        value="luke@skywalker.biz"
+      />
+    </div>
+    <div
+      className="form-field"
+    >
+      <label
+        htmlFor="test-email-subject"
+      >
+        email_configuration.test.subject
+      </label>
+      <input
+        className="settings-large-input"
+        disabled={true}
+        id="test-email-subject"
+        onChange={[Function]}
+        type="text"
+        value="email_configuration.test.subject"
+      />
+    </div>
+    <div
+      className="form-field"
+    >
+      <label
+        htmlFor="test-email-message"
+      >
+        email_configuration.test.message
+        <em
+          className="mandatory"
+        >
+          *
+        </em>
+      </label>
+      <textarea
+        className="settings-large-input"
+        disabled={true}
+        id="test-email-message"
+        onChange={[Function]}
+        required={true}
+        rows={5}
+        value="email_configuration.test.message_text"
+      />
+    </div>
+    <SubmitButton
+      disabled={true}
+    >
+      email_configuration.test.send
+    </SubmitButton>
+    <DeferredSpinner
+      className="spacer-left"
+    />
+  </form>
+</div>
+`;
+
+exports[`should render correctly: success 1`] = `
+<div
+  className="settings-definition"
+>
+  <div
+    className="settings-definition-left"
+  >
+    <h3
+      className="settings-definition-name"
+    >
+      email_configuration.test.title
+    </h3>
+  </div>
+  <form
+    className="settings-definition-right"
+    onSubmit={[Function]}
+  >
+    <div
+      className="form-field"
+    >
+      <Alert
+        variant="success"
+      >
+        email_configuration.test.email_was_sent_to_x.email@example.com
+      </Alert>
+    </div>
+    <div
+      className="form-field"
+    >
+      <label
+        htmlFor="test-email-to"
+      >
+        email_configuration.test.to_address
+        <em
+          className="mandatory"
+        >
+          *
+        </em>
+      </label>
+      <input
+        className="settings-large-input"
+        disabled={false}
+        id="test-email-to"
+        onChange={[Function]}
+        required={true}
+        type="email"
+        value="luke@skywalker.biz"
       />
     </div>
     <div