diff --git a/src/profile/ProfilePage.jsx b/src/profile/ProfilePage.jsx
index f0a140114..2dfe72dda 100644
--- a/src/profile/ProfilePage.jsx
+++ b/src/profile/ProfilePage.jsx
@@ -183,12 +183,19 @@ class ProfilePage extends React.Component {
visibilityBio,
requiresParentalConsent,
isLoadingProfile,
+ username,
+ saveState,
+ navigate,
} = this.props;
if (isLoadingProfile) {
return ;
}
+ if (!username && saveState === 'error' && navigate) {
+ navigate('/notfound');
+ }
+
const commonFormProps = {
openHandler: this.handleOpen,
closeHandler: this.handleClose,
@@ -330,6 +337,7 @@ ProfilePage.propTypes = {
// Account data
requiresParentalConsent: PropTypes.bool,
dateJoined: PropTypes.string,
+ username: PropTypes.string,
// Bio form data
bio: PropTypes.string,
@@ -395,6 +403,7 @@ ProfilePage.propTypes = {
openForm: PropTypes.func.isRequired,
closeForm: PropTypes.func.isRequired,
updateDraft: PropTypes.func.isRequired,
+ navigate: PropTypes.func.isRequired,
// Router
params: PropTypes.shape({
@@ -407,6 +416,7 @@ ProfilePage.propTypes = {
ProfilePage.defaultProps = {
saveState: null,
+ username: '',
savePhotoState: null,
photoUploadError: {},
profileImage: {},
diff --git a/src/profile/ProfilePage.test.jsx b/src/profile/ProfilePage.test.jsx
index 997a92e86..0b7c6be6e 100644
--- a/src/profile/ProfilePage.test.jsx
+++ b/src/profile/ProfilePage.test.jsx
@@ -9,6 +9,7 @@ import PropTypes from 'prop-types';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
+import { BrowserRouter, useNavigate } from 'react-router-dom';
import messages from '../i18n';
import ProfilePage from './ProfilePage';
@@ -16,6 +17,7 @@ import ProfilePage from './ProfilePage';
const mockStore = configureMockStore([thunk]);
const storeMocks = {
loadingApp: require('./__mocks__/loadingApp.mockStore'),
+ invalidUser: require('./__mocks__/invalidUser.mockStore'),
viewOwnProfile: require('./__mocks__/viewOwnProfile.mockStore'),
viewOtherProfile: require('./__mocks__/viewOtherProfile.mockStore'),
savingEditedBio: require('./__mocks__/savingEditedBio.mockStore'),
@@ -65,6 +67,23 @@ beforeEach(() => {
analytics.sendTrackingLogEvent.mockReset();
});
+const ProfileWrapper = ({ params, requiresParentalConsent }) => {
+ const navigate = useNavigate();
+ return (
+
+ );
+};
+
+ProfileWrapper.propTypes = {
+ params: PropTypes.shape({}).isRequired,
+ requiresParentalConsent: PropTypes.bool.isRequired,
+};
+
const ProfilePageWrapper = ({
contextValue, store, params, requiresParentalConsent,
}) => (
@@ -73,7 +92,12 @@ const ProfilePageWrapper = ({
>
-
+
+
+
@@ -103,6 +127,16 @@ describe('', () => {
expect(tree).toMatchSnapshot();
});
+ it('successfully redirected to not found page.', () => {
+ const contextValue = {
+ authenticatedUser: { userId: 123, username: 'staff', administrator: true },
+ config: getConfig(),
+ };
+ const component = ;
+ const { container: tree } = render(component);
+ expect(tree).toMatchSnapshot();
+ });
+
it('viewing own profile', () => {
const contextValue = {
authenticatedUser: { userId: 123, username: 'staff', administrator: true },
diff --git a/src/profile/__mocks__/invalidUser.mockStore.js b/src/profile/__mocks__/invalidUser.mockStore.js
new file mode 100644
index 000000000..5e45bb2ac
--- /dev/null
+++ b/src/profile/__mocks__/invalidUser.mockStore.js
@@ -0,0 +1,41 @@
+module.exports = {
+ userAccount: {
+ loading: false,
+ error: null,
+ username: 'staff',
+ email: null,
+ bio: null,
+ name: null,
+ country: null,
+ socialLinks: null,
+ profileImage: {
+ imageUrlMedium: null,
+ imageUrlLarge: null
+ },
+ levelOfEducation: null,
+ learningGoal: null
+ },
+ profilePage: {
+ errors: {},
+ saveState: 'error',
+ savePhotoState: null,
+ currentlyEditingField: null,
+ account: {
+ username: '',
+ socialLinks: []
+ },
+ preferences: {},
+ courseCertificates: [],
+ drafts: {},
+ isLoadingProfile: false,
+ isAuthenticatedUserProfile: true,
+ },
+ router: {
+ location: {
+ pathname: '/u/staffTest',
+ search: '',
+ hash: ''
+ },
+ action: 'POP'
+ }
+};
\ No newline at end of file
diff --git a/src/profile/__snapshots__/ProfilePage.test.jsx.snap b/src/profile/__snapshots__/ProfilePage.test.jsx.snap
index bb9c11d0d..7635041d8 100644
--- a/src/profile/__snapshots__/ProfilePage.test.jsx.snap
+++ b/src/profile/__snapshots__/ProfilePage.test.jsx.snap
@@ -29,6 +29,640 @@ exports[` Renders correctly in various states app loading 1`] = `
`;
+exports[` Renders correctly in various states successfully redirected to not found page. 1`] = `
+
+
+
+
+
+
+
+
+
+
+ staff
+
+
+
+
+
+
+ Your profile information is only visible to you. Only your username is visible to others on localhost.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ staff
+
+
+
+
+
+
+ Your profile information is only visible to you. Only your username is visible to others on localhost.
+
+
+
+
+
+
+
+
+
+
+
+ This is the name that appears in your account and on your certificates.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ You don't have any certificates yet.
+
+
+
+
+
+
+
+`;
+
exports[` Renders correctly in various states test country edit with error 1`] = `
{
return {
...state,
saveState: 'error',
+ isLoadingProfile: false,
errors: { ...state.errors, ...action.payload.errors },
};
case SAVE_PROFILE.RESET:
return {
...state,
saveState: null,
+ isLoadingProfile: false,
errors: {},
};
diff --git a/src/profile/data/sagas.js b/src/profile/data/sagas.js
index cfece88bb..7c9e409f8 100644
--- a/src/profile/data/sagas.js
+++ b/src/profile/data/sagas.js
@@ -1,4 +1,3 @@
-import { history } from '@edx/frontend-platform';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import pick from 'lodash.pick';
import {
@@ -95,7 +94,11 @@ export function* handleFetchProfile(action) {
yield put(fetchProfileReset());
} catch (e) {
if (e.response.status === 404) {
- history.push('/notfound');
+ if (e.processedData && e.processedData.fieldErrors) {
+ yield put(saveProfileFailure(e.processedData.fieldErrors));
+ } else {
+ yield put(saveProfileFailure(e.customAttributes));
+ }
} else {
throw e;
}
diff --git a/src/routes/AppRoutes.jsx b/src/routes/AppRoutes.jsx
index 1d46786b5..9fa2e22f2 100644
--- a/src/routes/AppRoutes.jsx
+++ b/src/routes/AppRoutes.jsx
@@ -3,15 +3,19 @@ import {
AuthenticatedPageRoute,
PageWrap,
} from '@edx/frontend-platform/react';
-import { Routes, Route } from 'react-router-dom';
+import { Routes, Route, useNavigate } from 'react-router-dom';
import { ProfilePage, NotFoundPage } from '../profile';
-const AppRoutes = () => (
-
- } />
- } />
- } />
-
-);
+const AppRoutes = () => {
+ const navigate = useNavigate();
+
+ return (
+
+ } />
+ } />
+ } />
+
+ );
+};
export default AppRoutes;