diff --git a/src/account-settings/delete-account/ConfirmationModal.jsx b/src/account-settings/delete-account/ConfirmationModal.jsx index 0b7580a..00a178f 100644 --- a/src/account-settings/delete-account/ConfirmationModal.jsx +++ b/src/account-settings/delete-account/ConfirmationModal.jsx @@ -10,7 +10,7 @@ import messages from './messages'; import { Alert } from '../../common'; import PrintingInstructions from './PrintingInstructions'; -class ConfirmationModal extends Component { +export class ConfirmationModal extends Component { /** * @returns String The message id for a short description of the error, suitable for a header or * as the error message under an input field. diff --git a/src/account-settings/delete-account/ConfirmationModal.test.jsx b/src/account-settings/delete-account/ConfirmationModal.test.jsx new file mode 100644 index 0000000..dd18294 --- /dev/null +++ b/src/account-settings/delete-account/ConfirmationModal.test.jsx @@ -0,0 +1,69 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import renderer from 'react-test-renderer'; +import { IntlProvider, injectIntl } from '@edx/frontend-i18n'; + +// Modal creates a portal. Overriding ReactDOM.createPortal allows portals to be tested in jest. +ReactDOM.createPortal = node => node; + +import { ConfirmationModal } from './ConfirmationModal'; // eslint-disable-line import/first + +const IntlConfirmationModal = injectIntl(ConfirmationModal); + +describe('ConfirmationModal', () => { + let props = {}; + + beforeEach(() => { + props = { + onCancel: jest.fn(), + onChange: jest.fn(), + onSubmit: jest.fn(), + status: null, + errorType: null, + password: 'fluffy bunnies', + logoutUrl: 'http://localhost/logout', + }; + }); + + it('should match default closed confirmation modal snapshot', () => { + const tree = renderer + .create(( + + + + )) + .toJSON(); + expect(tree).toMatchSnapshot(); + }); + + it('should match open confirmation modal snapshot', () => { + const tree = renderer + .create(( + + + + )) + .toJSON(); + expect(tree).toMatchSnapshot(); + }); + + it('should match empty password confirmation modal snapshot', () => { + const tree = renderer + .create(( + + + + )) + .toJSON(); + expect(tree).toMatchSnapshot(); + }); +}); diff --git a/src/account-settings/delete-account/DeleteAccount.jsx b/src/account-settings/delete-account/DeleteAccount.jsx index 30ab715..dd43ed0 100644 --- a/src/account-settings/delete-account/DeleteAccount.jsx +++ b/src/account-settings/delete-account/DeleteAccount.jsx @@ -17,12 +17,12 @@ import { import messages from './messages'; // Components -import ConfirmationModal from './ConfirmationModal'; +import ConnectedConfirmationModal from './ConfirmationModal'; import PrintingInstructions from './PrintingInstructions'; -import SuccessModal from './SuccessModal'; +import ConnectedSuccessModal from './SuccessModal'; import BeforeProceedingBanner from './BeforeProceedingBanner'; -class DeleteAccount extends React.Component { +export class DeleteAccount extends React.Component { state = { password: '', }; @@ -98,7 +98,7 @@ class DeleteAccount extends React.Component { /> ) : null} - - + ); } diff --git a/src/account-settings/delete-account/DeleteAccount.test.jsx b/src/account-settings/delete-account/DeleteAccount.test.jsx new file mode 100644 index 0000000..7006e69 --- /dev/null +++ b/src/account-settings/delete-account/DeleteAccount.test.jsx @@ -0,0 +1,71 @@ +import React from 'react'; +import renderer from 'react-test-renderer'; +import { IntlProvider, injectIntl } from '@edx/frontend-i18n'; + +// Testing the modals separately, they just clutter up the snapshots if included here. +jest.mock('./ConfirmationModal'); +jest.mock('./SuccessModal'); + +import { DeleteAccount } from './DeleteAccount'; // eslint-disable-line import/first + +const IntlDeleteAccount = injectIntl(DeleteAccount); + +describe('DeleteAccount', () => { + let props = {}; + + beforeEach(() => { + props = { + deleteAccount: jest.fn(), + deleteAccountConfirmation: jest.fn(), + deleteAccountFailure: jest.fn(), + deleteAccountReset: jest.fn(), + deleteAccountCancel: jest.fn(), + status: null, + errorType: null, + hasLinkedTPA: false, + isVerifiedAccount: true, + logoutUrl: 'http://localhost/logout', + }; + }); + + it('should match default section snapshot', () => { + const tree = renderer + .create(( + + + + )) + .toJSON(); + expect(tree).toMatchSnapshot(); + }); + + it('should match unverified account section snapshot', () => { + const tree = renderer + .create(( + + + + )) + .toJSON(); + expect(tree).toMatchSnapshot(); + }); + + it('should match unverified account section snapshot', () => { + const tree = renderer + .create(( + + + + )) + .toJSON(); + expect(tree).toMatchSnapshot(); + }); +}); diff --git a/src/account-settings/delete-account/SuccessModal.jsx b/src/account-settings/delete-account/SuccessModal.jsx index 610c933..dea4638 100644 --- a/src/account-settings/delete-account/SuccessModal.jsx +++ b/src/account-settings/delete-account/SuccessModal.jsx @@ -5,7 +5,7 @@ import { Modal } from '@edx/paragon'; import messages from './messages'; -const SuccessModal = (props) => { +export const SuccessModal = (props) => { const { status, intl, onClose } = props; return ( node; + +import { SuccessModal } from './SuccessModal'; // eslint-disable-line import/first + +const IntlSuccessModal = injectIntl(SuccessModal); + +describe('SuccessModal', () => { + let props = {}; + + beforeEach(() => { + props = { + onClose: jest.fn(), + status: null, + }; + }); + + it('should match default closed success modal snapshot', () => { + let tree = renderer.create(( + )) + .toJSON(); + expect(tree).toMatchSnapshot(); + + tree = renderer.create(( + )) + .toJSON(); + expect(tree).toMatchSnapshot(); + + tree = renderer.create(( + )) + .toJSON(); + expect(tree).toMatchSnapshot(); + + tree = renderer.create(( + )) + .toJSON(); + expect(tree).toMatchSnapshot(); + }); + + it('should match open success modal snapshot', () => { + const tree = renderer + .create(( + + + + )) + .toJSON(); + expect(tree).toMatchSnapshot(); + }); +}); diff --git a/src/account-settings/delete-account/__snapshots__/ConfirmationModal.test.jsx.snap b/src/account-settings/delete-account/__snapshots__/ConfirmationModal.test.jsx.snap new file mode 100644 index 0000000..1374a5c --- /dev/null +++ b/src/account-settings/delete-account/__snapshots__/ConfirmationModal.test.jsx.snap @@ -0,0 +1,436 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ConfirmationModal should match default closed confirmation modal snapshot 1`] = ` +
+
+
+
+
+
+

+ Are you sure? +

+
+
+
+
+
+ +
+
+
+ You have selected "Delete My Account". Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted. +
+

+ If you proceed, you will be unable to use this account to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employer's or university's system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School. +

+

+ + You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion, + + follow the instructions for printing or downloading a certificate + + . + +

+
+
+
+ + + + Unable to delete account + +
+
+
+
+ + +
+
+
+
+
+`; + +exports[`ConfirmationModal should match empty password confirmation modal snapshot 1`] = ` +
+
+
+
+
+
+

+ Are you sure? +

+
+
+
+
+
+ +
+
+
+ A password is required +
+

+ Sorry, there was an error trying to process your request. Please try again later. +

+
+
+
+
+ +
+
+
+ You have selected "Delete My Account". Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted. +
+

+ If you proceed, you will be unable to use this account to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employer's or university's system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School. +

+

+ + You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion, + + follow the instructions for printing or downloading a certificate + + . + +

+
+
+
+ + + + A password is required + +
+
+
+
+ + +
+
+
+
+
+`; + +exports[`ConfirmationModal should match open confirmation modal snapshot 1`] = ` +
+
+
+
+
+
+

+ Are you sure? +

+
+
+
+
+
+ +
+
+
+ You have selected "Delete My Account". Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted. +
+

+ If you proceed, you will be unable to use this account to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employer's or university's system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School. +

+

+ + You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion, + + follow the instructions for printing or downloading a certificate + + . + +

+
+
+
+ + + + Unable to delete account + +
+
+
+
+ + +
+
+
+
+
+`; diff --git a/src/account-settings/delete-account/__snapshots__/DeleteAccount.test.jsx.snap b/src/account-settings/delete-account/__snapshots__/DeleteAccount.test.jsx.snap new file mode 100644 index 0000000..2b7b0bc --- /dev/null +++ b/src/account-settings/delete-account/__snapshots__/DeleteAccount.test.jsx.snap @@ -0,0 +1,247 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DeleteAccount should match default section snapshot 1`] = ` +
+

+ Delete My Account +

+

+ We're sorry to see you go! +

+

+ Please note: Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted. +

+

+ Once your account is deleted, you cannot use it to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employer’s or university’s system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School. +

+

+ + You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion, + + follow the instructions for printing or downloading a certificate + + . + +

+

+ Warning: Account deletion is permanent. Please read the above carefully before proceeding. This is an irreversible action, and you will no longer be able to use the same email on edX. +

+

+ + Want to change your email, name, or password instead? + +

+

+ +

+
+`; + +exports[`DeleteAccount should match unverified account section snapshot 1`] = ` +
+

+ Delete My Account +

+

+ We're sorry to see you go! +

+

+ Please note: Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted. +

+

+ Once your account is deleted, you cannot use it to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employer’s or university’s system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School. +

+

+ + You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion, + + follow the instructions for printing or downloading a certificate + + . + +

+

+ Warning: Account deletion is permanent. Please read the above carefully before proceeding. This is an irreversible action, and you will no longer be able to use the same email on edX. +

+

+ + Want to change your email, name, or password instead? + +

+

+ +

+
+
+ +
+
+ + Before proceeding, please + + activate your account + + . + +
+
+
+`; + +exports[`DeleteAccount should match unverified account section snapshot 2`] = ` +
+

+ Delete My Account +

+

+ We're sorry to see you go! +

+

+ Please note: Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted. +

+

+ Once your account is deleted, you cannot use it to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employer’s or university’s system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School. +

+

+ + You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion, + + follow the instructions for printing or downloading a certificate + + . + +

+

+ Warning: Account deletion is permanent. Please read the above carefully before proceeding. This is an irreversible action, and you will no longer be able to use the same email on edX. +

+

+ + Want to change your email, name, or password instead? + +

+

+ +

+
+
+ +
+
+ + Before proceeding, please + + unlink all social media accounts + + . + +
+
+
+`; diff --git a/src/account-settings/delete-account/__snapshots__/SuccessModal.test.jsx.snap b/src/account-settings/delete-account/__snapshots__/SuccessModal.test.jsx.snap new file mode 100644 index 0000000..ab3b124 --- /dev/null +++ b/src/account-settings/delete-account/__snapshots__/SuccessModal.test.jsx.snap @@ -0,0 +1,306 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SuccessModal should match default closed success modal snapshot 1`] = ` +
+
+
+
+
+
+

+ We're sorry to see you go! Your account will be deleted shortly. +

+
+
+
+

+ Account deletion, including removal from email lists, may take a few weeks to fully process through our system. If you want to opt-out of emails before then, please unsubscribe from the footer of any email. +

+
+
+
+ +
+
+
+
+
+`; + +exports[`SuccessModal should match default closed success modal snapshot 2`] = ` +
+
+
+
+
+
+

+ We're sorry to see you go! Your account will be deleted shortly. +

+
+
+
+

+ Account deletion, including removal from email lists, may take a few weeks to fully process through our system. If you want to opt-out of emails before then, please unsubscribe from the footer of any email. +

+
+
+
+ +
+
+
+
+
+`; + +exports[`SuccessModal should match default closed success modal snapshot 3`] = ` +
+
+
+
+
+
+

+ We're sorry to see you go! Your account will be deleted shortly. +

+
+
+
+

+ Account deletion, including removal from email lists, may take a few weeks to fully process through our system. If you want to opt-out of emails before then, please unsubscribe from the footer of any email. +

+
+
+
+ +
+
+
+
+
+`; + +exports[`SuccessModal should match default closed success modal snapshot 4`] = ` +
+
+
+
+
+
+

+ We're sorry to see you go! Your account will be deleted shortly. +

+
+
+
+

+ Account deletion, including removal from email lists, may take a few weeks to fully process through our system. If you want to opt-out of emails before then, please unsubscribe from the footer of any email. +

+
+
+
+ +
+
+
+
+
+`; + +exports[`SuccessModal should match open success modal snapshot 1`] = ` +
+
+
+
+
+
+

+ We're sorry to see you go! Your account will be deleted shortly. +

+
+
+
+

+ Account deletion, including removal from email lists, may take a few weeks to fully process through our system. If you want to opt-out of emails before then, please unsubscribe from the footer of any email. +

+
+
+
+ +
+
+
+
+
+`; diff --git a/src/account-settings/delete-account/data/reducers.js b/src/account-settings/delete-account/data/reducers.js index e92b5b9..826ecc7 100644 --- a/src/account-settings/delete-account/data/reducers.js +++ b/src/account-settings/delete-account/data/reducers.js @@ -38,6 +38,7 @@ const reducer = (state = defaultState, action = null) => { return { ...state, + // clear the error state if applicable, otherwise don't change state status: oldStatus === 'failed' ? 'confirming' : oldStatus, errorType: null, }; diff --git a/src/account-settings/delete-account/data/reducers.test.js b/src/account-settings/delete-account/data/reducers.test.js new file mode 100644 index 0000000..132922e --- /dev/null +++ b/src/account-settings/delete-account/data/reducers.test.js @@ -0,0 +1,107 @@ +import reducer from './reducers'; +import { + deleteAccountConfirmation, + deleteAccountBegin, + deleteAccountSuccess, + deleteAccountFailure, + deleteAccountReset, + deleteAccountCancel, +} from './actions'; + +describe('delete-account reducer', () => { + let state = null; + + beforeEach(() => { + state = reducer(); + }); + + it('should process DELETE_ACCOUNT.CONFIRMATION', () => { + const result = reducer(state, deleteAccountConfirmation()); + expect(result).toEqual({ + errorType: null, + status: 'confirming', + }); + }); + + it('should process DELETE_ACCOUNT.BEGIN', () => { + const result = reducer(state, deleteAccountBegin()); + expect(result).toEqual({ + errorType: null, + status: 'pending', + }); + }); + + it('should process DELETE_ACCOUNT.SUCCESS', () => { + const result = reducer(state, deleteAccountSuccess()); + expect(result).toEqual({ + errorType: null, + status: 'deleted', + }); + }); + + it('should process DELETE_ACCOUNT.FAILURE no reason', () => { + const result = reducer(state, deleteAccountFailure()); + expect(result).toEqual({ + errorType: 'server', + status: 'failed', + }); + }); + + it('should process DELETE_ACCOUNT.FAILURE with reason', () => { + const result = reducer(state, deleteAccountFailure('carnivorous buns')); + expect(result).toEqual({ + errorType: 'carnivorous buns', + status: 'failed', + }); + }); + + it('should process DELETE_ACCOUNT.RESET no status', () => { + const result = reducer(state, deleteAccountReset()); + expect(result).toEqual({ + errorType: null, + status: null, + }); + }); + + it('should process DELETE_ACCOUNT.RESET with failed old status', () => { + const result = reducer( + { + errorType: 'carnivorous buns', + status: 'failed', + }, + deleteAccountReset(), + ); + expect(result).toEqual({ + errorType: null, + status: 'confirming', + }); + }); + + it('should process DELETE_ACCOUNT.RESET with pending old status', () => { + const result = reducer( + { + errorType: 'carnivorous buns', + status: 'pending', + }, + deleteAccountReset(), + ); + expect(result).toEqual({ + errorType: null, + status: 'pending', + }); + }); + + it('should process DELETE_ACCOUNT.CANCEL', () => { + const result = reducer( + { + errorType: 'carnivorous buns', + status: 'failed', + }, + deleteAccountCancel(), + ); + expect(result).toEqual({ + errorType: null, + status: null, + }); + }); +});