diff --git a/src/certificates/certificate-create-form/CertificateCreateForm.jsx b/src/certificates/certificate-create-form/CertificateCreateForm.jsx
index 31a1271b76..e608e963d7 100644
--- a/src/certificates/certificate-create-form/CertificateCreateForm.jsx
+++ b/src/certificates/certificate-create-form/CertificateCreateForm.jsx
@@ -1,5 +1,6 @@
import PropTypes from 'prop-types';
import { Card, Stack, Button } from '@edx/paragon';
+import { useIntl } from '@edx/frontend-platform/i18n';
import { Formik, Form, FieldArray } from 'formik';
import CertificateDetailsForm from '../certificate-details/CertificateDetailsForm';
@@ -9,8 +10,9 @@ import messages from '../messages';
import useCertificateCreateForm from './hooks/useCertificateCreateForm';
const CertificateCreateForm = ({ courseId }) => {
+ const intl = useIntl();
const {
- intl, courseTitle, handleCertificateSubmit, handleFormCancel,
+ courseTitle, handleCertificateSubmit, handleFormCancel,
} = useCertificateCreateForm(courseId);
return (
diff --git a/src/certificates/certificate-create-form/hooks/useCertificateCreateForm.jsx b/src/certificates/certificate-create-form/hooks/useCertificateCreateForm.jsx
index 5d895de5fe..129427059c 100644
--- a/src/certificates/certificate-create-form/hooks/useCertificateCreateForm.jsx
+++ b/src/certificates/certificate-create-form/hooks/useCertificateCreateForm.jsx
@@ -1,5 +1,4 @@
import { useSelector, useDispatch } from 'react-redux';
-import { useIntl } from '@edx/frontend-platform/i18n';
import { MODE_STATES } from '../../data/constants';
import { getCourseTitle } from '../../data/selectors';
@@ -7,7 +6,6 @@ import { setMode } from '../../data/slice';
import { createCourseCertificate } from '../../data/thunks';
const useCertificateCreateForm = (courseId) => {
- const intl = useIntl();
const dispatch = useDispatch();
const courseTitle = useSelector(getCourseTitle);
@@ -21,7 +19,7 @@ const useCertificateCreateForm = (courseId) => {
window.scrollTo({ top: 0, behavior: 'smooth' });
};
return {
- intl, courseTitle, handleCertificateSubmit, handleFormCancel,
+ courseTitle, handleCertificateSubmit, handleFormCancel,
};
};
diff --git a/src/certificates/certificate-edit-form/CertificateEditForm.jsx b/src/certificates/certificate-edit-form/CertificateEditForm.jsx
index af1d9a4c30..b2225b1816 100644
--- a/src/certificates/certificate-edit-form/CertificateEditForm.jsx
+++ b/src/certificates/certificate-edit-form/CertificateEditForm.jsx
@@ -1,5 +1,6 @@
import PropTypes from 'prop-types';
import { Card, Stack, Button } from '@edx/paragon';
+import { useIntl } from '@edx/frontend-platform/i18n';
import { Formik, Form, FieldArray } from 'formik';
import ModalNotification from '../../generic/modal-notification';
@@ -10,8 +11,8 @@ import messages from '../certificate-details/messages';
import useCertificateEditForm from './hooks/useCertificateEditForm';
const CertificateEditForm = ({ courseId }) => {
+ const intl = useIntl();
const {
- intl,
confirmOpen,
courseTitle,
certificates,
diff --git a/src/certificates/certificate-edit-form/CertificateEditForm.test.jsx b/src/certificates/certificate-edit-form/CertificateEditForm.test.jsx
new file mode 100644
index 0000000000..df2ff4bde0
--- /dev/null
+++ b/src/certificates/certificate-edit-form/CertificateEditForm.test.jsx
@@ -0,0 +1,117 @@
+import { Provider } from 'react-redux';
+import { render, waitFor, within } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { initializeMockApp } from '@edx/frontend-platform';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+import MockAdapter from 'axios-mock-adapter';
+import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
+
+import { executeThunk } from '../../utils';
+import initializeStore from '../../store';
+import { getCertificatesApiUrl, getUpdateCertificateApiUrl } from '../data/api';
+import { fetchCertificates, deleteCourseCertificate, updateCourseCertificate } from '../data/thunks';
+import { certificatesDataMock } from '../__mocks__';
+import { MODE_STATES } from '../data/constants';
+import messagesDetails from '../certificate-details/messages';
+import messages from '../messages';
+import CertificateEditForm from './CertificateEditForm';
+
+let axiosMock;
+let store;
+const courseId = 'course-123';
+
+jest.mock('@edx/frontend-platform/i18n', () => ({
+ ...jest.requireActual('@edx/frontend-platform/i18n'),
+ useIntl: () => ({
+ formatMessage: (message) => message.defaultMessage,
+ }),
+}));
+
+const renderComponent = () => render(
+
+
+
+
+ ,
+);
+
+const initialState = {
+ certificates: {
+ certificatesData: {},
+ componentMode: MODE_STATES.editAll,
+ },
+};
+
+describe('CertificatesList Component', () => {
+ beforeEach(async () => {
+ initializeMockApp({
+ authenticatedUser: {
+ userId: 3,
+ username: 'abc123',
+ administrator: true,
+ roles: [],
+ },
+ });
+ store = initializeStore(initialState);
+ axiosMock = new MockAdapter(getAuthenticatedHttpClient());
+ axiosMock
+ .onGet(getCertificatesApiUrl(courseId))
+ .reply(200, certificatesDataMock);
+ await executeThunk(fetchCertificates(courseId), store.dispatch);
+ });
+
+ it('submits the form with updated certificate details', async () => {
+ const courseTitleOverrideValue = 'Updated Course Title';
+ const signatoryNameValue = 'Updated signatory name';
+ const newCertificateData = {
+ ...certificatesDataMock,
+ courseTitle: courseTitleOverrideValue,
+ certificates: [{
+ ...certificatesDataMock.certificates[0],
+ signatories: [{
+ ...certificatesDataMock.certificates[0].signatories[0],
+ name: signatoryNameValue,
+ }],
+ }],
+ };
+
+ const { getByDisplayValue, getByRole, getByPlaceholderText } = renderComponent();
+
+ userEvent.type(
+ getByPlaceholderText(messagesDetails.detailsCourseTitleOverride.defaultMessage),
+ courseTitleOverrideValue,
+ );
+
+ userEvent.click(getByRole('button', { name: messages.saveTooltip.defaultMessage }));
+
+ axiosMock.onPost(
+ getUpdateCertificateApiUrl(courseId, certificatesDataMock.certificates[0].id),
+ ).reply(200, newCertificateData);
+ await executeThunk(updateCourseCertificate(courseId, newCertificateData), store.dispatch);
+
+ await waitFor(() => {
+ expect(getByDisplayValue(
+ certificatesDataMock.certificates[0].courseTitle + courseTitleOverrideValue,
+ )).toBeInTheDocument();
+ });
+ });
+
+ it('deletes a certificate and updates the store', async () => {
+ axiosMock.onDelete(
+ getUpdateCertificateApiUrl(courseId, certificatesDataMock.certificates[0].id),
+ ).reply(200);
+
+ const { getByRole } = renderComponent();
+
+ userEvent.click(getByRole('button', { name: messages.deleteTooltip.defaultMessage }));
+
+ const confirmDeleteModal = getByRole('dialog');
+ userEvent.click(within(confirmDeleteModal).getByRole('button', { name: messages.deleteTooltip.defaultMessage }));
+
+ await executeThunk(deleteCourseCertificate(courseId, certificatesDataMock.certificates[0].id), store.dispatch);
+
+ await waitFor(() => {
+ expect(store.getState().certificates.certificatesData.certificates.length).toBe(0);
+ });
+ });
+});
diff --git a/src/certificates/certificate-edit-form/hooks/useCertificateEditForm.jsx b/src/certificates/certificate-edit-form/hooks/useCertificateEditForm.jsx
index f71b62eabc..7e607d5217 100644
--- a/src/certificates/certificate-edit-form/hooks/useCertificateEditForm.jsx
+++ b/src/certificates/certificate-edit-form/hooks/useCertificateEditForm.jsx
@@ -1,4 +1,3 @@
-import { useIntl } from '@edx/frontend-platform/i18n';
import { useSelector, useDispatch } from 'react-redux';
import { useToggle } from '@edx/paragon';
@@ -9,7 +8,6 @@ import { updateCourseCertificate, deleteCourseCertificate } from '../../data/thu
import { defaultCertificate } from '../../constants';
const useCertificateEditForm = (courseId) => {
- const intl = useIntl();
const dispatch = useDispatch();
const [isConfirmOpen, confirmOpen, confirmClose] = useToggle(false);
const courseTitle = useSelector(getCourseTitle);
@@ -36,7 +34,6 @@ const useCertificateEditForm = (courseId) => {
}));
return {
- intl,
confirmOpen,
courseTitle,
certificates,
diff --git a/src/certificates/certificate-signatories/CertificateSignatories.jsx b/src/certificates/certificate-signatories/CertificateSignatories.jsx
index 7d388e8a66..138867b72c 100644
--- a/src/certificates/certificate-signatories/CertificateSignatories.jsx
+++ b/src/certificates/certificate-signatories/CertificateSignatories.jsx
@@ -10,7 +10,15 @@ import useCreateSignatory from './hooks/useCreateSignatory';
import messages from './messages';
const CertificateSignatories = ({
- signatories, handleChange, handleBlur, arrayHelpers, setFieldValue, isForm, resetForm, editModes, setEditModes,
+ isForm,
+ editModes,
+ signatories,
+ arrayHelpers,
+ initialSignatoriesValues,
+ setFieldValue,
+ setEditModes,
+ handleBlur,
+ handleChange,
}) => {
const intl = useIntl();
@@ -19,7 +27,7 @@ const CertificateSignatories = ({
handleDeleteSignatory,
handleCancelUpdateSignatory,
} = useEditSignatory({
- arrayHelpers, editModes, setEditModes, resetForm,
+ arrayHelpers, editModes, setEditModes, setFieldValue, initialSignatoriesValues,
});
const { handleAddSignatory } = useCreateSignatory({ arrayHelpers });
@@ -90,18 +98,23 @@ CertificateSignatories.defaultProps = {
handleBlur: null,
setFieldValue: null,
arrayHelpers: null,
- resetForm: null,
isForm: false,
editModes: {},
setEditModes: null,
+ initialSignatoriesValues: null,
};
CertificateSignatories.propTypes = {
isForm: PropTypes.bool,
editModes: PropTypes.objectOf(PropTypes.bool),
+ initialSignatoriesValues: PropTypes.arrayOf(PropTypes.shape({
+ name: PropTypes.string.isRequired,
+ organization: PropTypes.string.isRequired,
+ signatureImagePath: PropTypes.string.isRequired,
+ title: PropTypes.string.isRequired,
+ })),
handleChange: PropTypes.func,
handleBlur: PropTypes.func,
- resetForm: PropTypes.func,
setFieldValue: PropTypes.func,
setEditModes: PropTypes.func,
arrayHelpers: PropTypes.shape({}),
diff --git a/src/certificates/certificate-signatories/hooks/useEditSignatory.jsx b/src/certificates/certificate-signatories/hooks/useEditSignatory.jsx
index 44928f9563..5784169237 100644
--- a/src/certificates/certificate-signatories/hooks/useEditSignatory.jsx
+++ b/src/certificates/certificate-signatories/hooks/useEditSignatory.jsx
@@ -1,5 +1,5 @@
const useEditSignatory = ({
- arrayHelpers, editModes, setEditModes, resetForm,
+ arrayHelpers, editModes, setEditModes, setFieldValue, initialSignatoriesValues,
}) => {
const handleDeleteSignatory = (id) => {
arrayHelpers.remove(id);
@@ -19,8 +19,12 @@ const useEditSignatory = ({
};
const handleCancelUpdateSignatory = (id) => {
+ const signatoryInitialValues = initialSignatoriesValues[id];
+ Object.keys(signatoryInitialValues).forEach(fieldKey => {
+ const fieldName = `signatories[${id}].${fieldKey}`;
+ setFieldValue(fieldName, signatoryInitialValues[fieldKey]);
+ });
toggleEditSignatory(id);
- resetForm();
};
return { toggleEditSignatory, handleDeleteSignatory, handleCancelUpdateSignatory };
diff --git a/src/certificates/certificates-list/CertificatesList.jsx b/src/certificates/certificates-list/CertificatesList.jsx
index bcd15703d9..69a3fb557b 100644
--- a/src/certificates/certificates-list/CertificatesList.jsx
+++ b/src/certificates/certificates-list/CertificatesList.jsx
@@ -23,7 +23,7 @@ const CertificatesList = ({ courseId }) => {
{certificates.map((certificate, idx) => (
{({
- values, handleChange, handleBlur, resetForm, setFieldValue,
+ values, handleChange, handleBlur, setFieldValue,
}) => (