From ccd2a5c074b1aa4e84ae34c0b385fcd117709832 Mon Sep 17 00:00:00 2001
From: Bianca Severino
Date: Thu, 18 Mar 2021 15:51:22 -0400
Subject: [PATCH] Revert "[MST-535] Prevent IDV name change if name is managed
by a third party"
---
.../IdVerification.messages.js | 5 -
.../IdVerificationContextProvider.jsx | 101 ++++++++----------
src/id-verification/panels/GetNameIdPanel.jsx | 54 +++-------
.../panels/ReviewRequirementsPanel.jsx | 23 +---
src/id-verification/panels/SummaryPanel.jsx | 62 ++++-------
.../IdVerificationContextProvider.test.jsx | 32 +-----
.../tests/panels/GetNameIdPanel.test.jsx | 58 ++++------
.../panels/ReviewRequirementsPanel.test.jsx | 45 ++++----
.../tests/panels/SummaryPanel.test.jsx | 7 --
9 files changed, 121 insertions(+), 266 deletions(-)
diff --git a/src/id-verification/IdVerification.messages.js b/src/id-verification/IdVerification.messages.js
index 823f781..51a4edf 100644
--- a/src/id-verification/IdVerification.messages.js
+++ b/src/id-verification/IdVerification.messages.js
@@ -6,11 +6,6 @@ const messages = defineMessages({
defaultMessage: 'Next',
description: 'Next button.',
},
- 'id.verification.support': {
- id: 'id.verification.support',
- defaultMessage: 'support',
- description: 'Website support.',
- },
'id.verification.example.card.alt': {
id: 'id.verification.example.card.alt',
defaultMessage: 'Example of a valid identification card with a full name and photo.',
diff --git a/src/id-verification/IdVerificationContextProvider.jsx b/src/id-verification/IdVerificationContextProvider.jsx
index e296246..fb6ab52 100644
--- a/src/id-verification/IdVerificationContextProvider.jsx
+++ b/src/id-verification/IdVerificationContextProvider.jsx
@@ -2,74 +2,24 @@ import React, { useState, useContext, useEffect } from 'react';
import PropTypes from 'prop-types';
import { AppContext } from '@edx/frontend-platform/react';
-import { getProfileDataManager } from '../account-settings/data/service';
-import PageLoading from '../account-settings/PageLoading';
-
-import { getExistingIdVerification, getEnrollments } from './data/service';
-import AccessBlocked from './AccessBlocked';
import { hasGetUserMediaSupport } from './getUserMediaShim';
+import { getExistingIdVerification, getEnrollments } from './data/service';
+import PageLoading from '../account-settings/PageLoading';
+import AccessBlocked from './AccessBlocked';
import IdVerificationContext, { MEDIA_ACCESS, ERROR_REASONS, VERIFIED_MODES } from './IdVerificationContext';
export default function IdVerificationContextProvider({ children }) {
- const { authenticatedUser } = useContext(AppContext);
-
const [existingIdVerification, setExistingIdVerification] = useState(null);
- useEffect(() => {
- // Call verification status endpoint to check whether we can verify.
- (async () => {
- const existingIdV = await getExistingIdVerification();
- setExistingIdVerification(existingIdV);
- })();
- }, []);
-
const [facePhotoFile, setFacePhotoFile] = useState(null);
const [idPhotoFile, setIdPhotoFile] = useState(null);
const [idPhotoName, setIdPhotoName] = useState(null);
const [mediaStream, setMediaStream] = useState(null);
- const [mediaAccess, setMediaAccess] = useState(
- hasGetUserMediaSupport ? MEDIA_ACCESS.PENDING : MEDIA_ACCESS.UNSUPPORTED,
- );
-
+ const [mediaAccess, setMediaAccess] = useState(hasGetUserMediaSupport
+ ? MEDIA_ACCESS.PENDING
+ : MEDIA_ACCESS.UNSUPPORTED);
const [canVerify, setCanVerify] = useState(true);
const [error, setError] = useState('');
- useEffect(() => {
- // Check for an existing verification attempt
- if (existingIdVerification && !existingIdVerification.canVerify) {
- const { status } = existingIdVerification;
- setCanVerify(false);
- if (status === 'pending' || status === 'approved') {
- setError(ERROR_REASONS.EXISTING_REQUEST);
- } else {
- setError(ERROR_REASONS.CANNOT_VERIFY);
- }
- }
- }, [existingIdVerification]);
- useEffect(() => {
- // Check whether the learner is enrolled in a verified course mode.
- (async () => {
- /* eslint-disable arrow-body-style */
- const enrollments = await getEnrollments();
- const verifiedEnrollments = enrollments.filter((enrollment) => {
- return VERIFIED_MODES.includes(enrollment.mode);
- });
- if (verifiedEnrollments.length === 0) {
- setCanVerify(false);
- setError(ERROR_REASONS.COURSE_ENROLLMENT);
- }
- })();
- }, []);
-
- const [profileDataManager, setProfileDataManager] = useState(null);
- useEffect(() => {
- // Determine if the user's profile data is managed by a third-party identity provider.
- // If so, they cannot update their account name manually.
- if (authenticatedUser.roles.length > 0) {
- setProfileDataManager(
- getProfileDataManager(authenticatedUser.username, authenticatedUser.roles),
- );
- }
- }, [authenticatedUser]);
-
+ const { authenticatedUser } = useContext(AppContext);
const [optimizelyExperimentName, setOptimizelyExperimentName] = useState('');
const contextValue = {
@@ -81,7 +31,6 @@ export default function IdVerificationContextProvider({ children }) {
mediaAccess,
userId: authenticatedUser.userId,
nameOnAccount: authenticatedUser.name,
- profileDataManager,
optimizelyExperimentName,
setExistingIdVerification,
setFacePhotoFile,
@@ -109,6 +58,42 @@ export default function IdVerificationContextProvider({ children }) {
},
};
+ useEffect(() => {
+ // Call verification status endpoint to check whether we can verify.
+ (async () => {
+ const existingIdV = await getExistingIdVerification();
+ setExistingIdVerification(existingIdV);
+ })();
+ }, []);
+
+ useEffect(() => {
+ // Check whether the learner is enrolled in a verified course mode.
+ (async () => {
+ /* eslint-disable arrow-body-style */
+ const enrollments = await getEnrollments();
+ const verifiedEnrollments = enrollments.filter((enrollment) => {
+ return VERIFIED_MODES.includes(enrollment.mode);
+ });
+ if (verifiedEnrollments.length === 0) {
+ setCanVerify(false);
+ setError(ERROR_REASONS.COURSE_ENROLLMENT);
+ }
+ })();
+ }, []);
+
+ useEffect(() => {
+ // Check for an existing verification attempt
+ if (existingIdVerification && !existingIdVerification.canVerify) {
+ const { status } = existingIdVerification;
+ setCanVerify(false);
+ if (status === 'pending' || status === 'approved') {
+ setError(ERROR_REASONS.EXISTING_REQUEST);
+ } else {
+ setError(ERROR_REASONS.CANNOT_VERIFY);
+ }
+ }
+ }, [existingIdVerification]);
+
// If we are waiting for verification status endpoint, show spinner.
if (!existingIdVerification) {
return ;
diff --git a/src/id-verification/panels/GetNameIdPanel.jsx b/src/id-verification/panels/GetNameIdPanel.jsx
index ef9b6e0..e66edd0 100644
--- a/src/id-verification/panels/GetNameIdPanel.jsx
+++ b/src/id-verification/panels/GetNameIdPanel.jsx
@@ -1,11 +1,10 @@
import React, {
useContext, useState, useEffect, useRef,
} from 'react';
-import { getConfig } from '@edx/frontend-platform';
-import { Hyperlink, Form } from '@edx/paragon';
+import { Form } from '@edx/paragon';
import { Link, useHistory } from 'react-router-dom';
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
-import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
+import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { useNextPanelSlug } from '../routing-utilities';
import BasePanel from './BasePanel';
@@ -21,14 +20,14 @@ function GetNameIdPanel(props) {
const nextPanelSlug = useNextPanelSlug(panelSlug);
const {
- nameOnAccount, userId, profileDataManager, idPhotoName, setIdPhotoName,
+ nameOnAccount, userId, idPhotoName, setIdPhotoName,
} = useContext(IdVerificationContext);
const nameOnAccountValue = nameOnAccount || '';
const invalidName = !nameMatches && (!idPhotoName || idPhotoName === nameOnAccount);
const blankName = !nameOnAccount && !idPhotoName;
useEffect(() => {
- setIdPhotoName(null);
+ setIdPhotoName('');
}, []);
useEffect(() => {
@@ -46,39 +45,6 @@ function GetNameIdPanel(props) {
}
}, [nameMatches, blankName]);
- const getNameValue = () => {
- if (!nameMatches) {
- // If we just check that idPhotoName exists, an empty string will cause
- // the field to reset to nameOnAccountValue, so check that it is a string here.
- if (typeof idPhotoName === 'string') {
- return idPhotoName;
- }
- return nameOnAccountValue;
- }
- return nameOnAccountValue;
- };
-
- const getErrorMessage = () => {
- if (profileDataManager) {
- return (
- {profileDataManager},
- support: (
-
- {props.intl.formatMessage(messages['id.verification.support'])}
-
- ),
- }}
- />
- );
- }
- return props.intl.formatMessage(messages['id.verification.account.name.error']);
- };
-
const handleSubmit = (e) => {
e.preventDefault();
// If the input is empty, or if no changes have been made to the
@@ -114,7 +80,7 @@ function GetNameIdPanel(props) {
inline
onChange={() => {
setNameMatches(true);
- setIdPhotoName(null);
+ setIdPhotoName('');
}}
/>
setIdPhotoName(e.target.value)}
data-testid="name-input"
/>
- {getErrorMessage()}
+ {props.intl.formatMessage(messages['id.verification.account.name.error'])}
diff --git a/src/id-verification/panels/ReviewRequirementsPanel.jsx b/src/id-verification/panels/ReviewRequirementsPanel.jsx
index dbcf7a1..a7139c1 100644
--- a/src/id-verification/panels/ReviewRequirementsPanel.jsx
+++ b/src/id-verification/panels/ReviewRequirementsPanel.jsx
@@ -1,9 +1,7 @@
import React, { useEffect, useContext } from 'react';
import { Link } from 'react-router-dom';
-import { getConfig } from '@edx/frontend-platform';
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
-import { Alert, Hyperlink } from '@edx/paragon';
import { useNextPanelSlug } from '../routing-utilities';
import BasePanel from './BasePanel';
@@ -13,9 +11,7 @@ import messages from '../IdVerification.messages';
import exampleCard from '../assets/example-card.png';
function ReviewRequirementsPanel(props) {
- const {
- userId, profileDataManager, setOptimizelyExperimentName,
- } = useContext(IdVerificationContext);
+ const { userId, setOptimizelyExperimentName } = useContext(IdVerificationContext);
const panelSlug = 'review-requirements';
const nextPanelSlug = useNextPanelSlug(panelSlug);
@@ -46,23 +42,6 @@ function ReviewRequirementsPanel(props) {
title={props.intl.formatMessage(messages['id.verification.requirements.title'])}
focusOnMount={false}
>
- {profileDataManager && (
-
- {profileDataManager},
- support: (
-
- {props.intl.formatMessage(messages['id.verification.support'])}
-
- ),
- }}
- />
-
- )}
{props.intl.formatMessage(messages['id.verification.requirements.description'])}
diff --git a/src/id-verification/panels/SummaryPanel.jsx b/src/id-verification/panels/SummaryPanel.jsx
index 7bed5c9..04b27b6 100644
--- a/src/id-verification/panels/SummaryPanel.jsx
+++ b/src/id-verification/panels/SummaryPanel.jsx
@@ -1,7 +1,7 @@
import React, { useState, useContext } from 'react';
-import { getConfig, history } from '@edx/frontend-platform';
+import { history } from '@edx/frontend-platform';
import {
- Alert, Hyperlink, Input, Button, Spinner,
+ Input, Button, Spinner, Alert,
} from '@edx/paragon';
import { Link } from 'react-router-dom';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
@@ -21,7 +21,6 @@ function SummaryPanel(props) {
const {
facePhotoFile,
idPhotoFile,
- profileDataManager,
nameOnAccount,
idPhotoName,
stopUserMedia,
@@ -112,7 +111,7 @@ function SummaryPanel(props) {
-
-
+
{props.intl.formatMessage(messages['id.verification.review.id.label'])}
-
+
{props.intl.formatMessage(messages['id.verification.account.name.label'])}
- {profileDataManager && (
-
- {profileDataManager},
- support: (
-
- {props.intl.formatMessage(messages['id.verification.support'])}
-
- ),
- }}
- />
-
- )}
{}}
- aria-describedby={profileDataManager ? 'profile-manager-warning' : null}
/>
- {!profileDataManager && (
-
+ Account Name,
}}
- >
- Account Name,
- }}
- />
-
- )}
+ />
+
{' '}
diff --git a/src/id-verification/tests/IdVerificationContextProvider.test.jsx b/src/id-verification/tests/IdVerificationContextProvider.test.jsx
index 619bfdc..a186599 100644
--- a/src/id-verification/tests/IdVerificationContextProvider.test.jsx
+++ b/src/id-verification/tests/IdVerificationContextProvider.test.jsx
@@ -1,19 +1,11 @@
import React from 'react';
import { render, cleanup, act } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
-
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { AppContext } from '@edx/frontend-platform/react';
-
-import { getProfileDataManager } from '../../account-settings/data/service';
-
import { getExistingIdVerification, getEnrollments } from '../data/service';
import IdVerificationContextProvider from '../IdVerificationContextProvider';
-jest.mock('../../account-settings/data/service', () => ({
- getProfileDataManager: jest.fn(),
-}));
-
jest.mock('../data/service', () => ({
getExistingIdVerification: jest.fn(),
getEnrollments: jest.fn(() => []),
@@ -30,9 +22,8 @@ describe('IdVerificationContextProvider', () => {
});
it('renders correctly and calls getExistingIdVerification + getEnrollments', async () => {
- const context = { authenticatedUser: { userId: 3, roles: [] } };
await act(async () => render((
-
+
@@ -41,25 +32,4 @@ describe('IdVerificationContextProvider', () => {
expect(getExistingIdVerification).toHaveBeenCalled();
expect(getEnrollments).toHaveBeenCalled();
});
-
- it('calls getProfileDataManager if the user has any roles', async () => {
- const context = {
- authenticatedUser: {
- userId: 3,
- username: 'testname',
- roles: ['enterprise_learner'],
- },
- };
- await act(async () => render((
-
-
-
-
-
- )));
- expect(getProfileDataManager).toHaveBeenCalledWith(
- context.authenticatedUser.username,
- context.authenticatedUser.roles,
- );
- });
});
diff --git a/src/id-verification/tests/panels/GetNameIdPanel.test.jsx b/src/id-verification/tests/panels/GetNameIdPanel.test.jsx
index 86544c7..814ef4e 100644
--- a/src/id-verification/tests/panels/GetNameIdPanel.test.jsx
+++ b/src/id-verification/tests/panels/GetNameIdPanel.test.jsx
@@ -31,7 +31,11 @@ describe('GetNameIdPanel', () => {
idPhotoFile: 'test.jpg',
};
- const getPanel = async () => {
+ afterEach(() => {
+ cleanup();
+ });
+
+ it('edits', async () => {
await act(async () => render((
@@ -41,29 +45,16 @@ describe('GetNameIdPanel', () => {
)));
- };
-
- afterEach(() => {
- cleanup();
- });
-
- it('edits', async () => {
- await getPanel();
-
const yesButton = await screen.findByTestId('name-matches-yes');
const noButton = await screen.findByTestId('name-matches-no');
const input = await screen.findByTestId('name-input');
const nextButton = await screen.findByTestId('next-button');
-
expect(input).toHaveProperty('readOnly');
-
fireEvent.click(noButton);
expect(input).toHaveProperty('readOnly', false);
expect(nextButton.classList.contains('disabled')).toBe(true);
-
fireEvent.change(input, { target: { value: 'test change' } });
expect(contextValue.setIdPhotoName).toHaveBeenCalled();
-
fireEvent.click(yesButton);
expect(input).toHaveProperty('readOnly');
expect(contextValue.setIdPhotoName).toHaveBeenCalled();
@@ -71,39 +62,36 @@ describe('GetNameIdPanel', () => {
it('disables radio buttons + next button and enables input if account name is blank', async () => {
contextValue.nameOnAccount = '';
- await getPanel();
-
+ await act(async () => render((
+
+
+
+
+
+
+
+ )));
const yesButton = await screen.findByTestId('name-matches-yes');
const noButton = await screen.findByTestId('name-matches-no');
const input = await screen.findByTestId('name-input');
const nextButton = await screen.findByTestId('next-button');
-
expect(yesButton).toHaveProperty('disabled');
expect(noButton).toHaveProperty('disabled');
expect(input).toHaveProperty('readOnly', false);
expect(nextButton.classList.contains('disabled')).toBe(true);
});
- it('blocks the user from changing account name if managed by a third party', async () => {
- contextValue.profileDataManager = 'test-org';
- await getPanel();
-
- const noButton = await screen.findByTestId('name-matches-no');
- const input = await screen.findByTestId('name-input');
- const nextButton = await screen.findByTestId('next-button');
-
- fireEvent.click(noButton);
- expect(input).toHaveProperty('readOnly');
- expect(nextButton.classList.contains('disabled')).toBe(true);
- const warning = await screen.getAllByText('test-org');
- expect(warning.length).toEqual(2);
- });
-
it('routes to SummaryPanel', async () => {
- await getPanel();
-
+ await act(async () => render((
+
+
+
+
+
+
+
+ )));
const button = await screen.findByTestId('next-button');
-
fireEvent.click(button);
expect(history.location.pathname).toEqual('/summary');
});
diff --git a/src/id-verification/tests/panels/ReviewRequirementsPanel.test.jsx b/src/id-verification/tests/panels/ReviewRequirementsPanel.test.jsx
index 46d708b..30307e1 100644
--- a/src/id-verification/tests/panels/ReviewRequirementsPanel.test.jsx
+++ b/src/id-verification/tests/panels/ReviewRequirementsPanel.test.jsx
@@ -24,7 +24,26 @@ describe('ReviewRequirementsPanel', () => {
const context = { setOptimizelyExperimentName: jest.fn() };
- const getPanel = async () => {
+ afterEach(() => {
+ cleanup();
+ });
+
+ it('routes to RequestCameraAccessPanel', async () => {
+ await act(async () => render((
+
+
+
+
+
+ )));
+ const button = await screen.findByTestId('next-button');
+ fireEvent.click(button);
+ expect(history.location.pathname).toEqual('/request-camera-access');
+ });
+
+ it('updates optimizely experiment name in context', async () => {
+ window.experimentVariables = {};
+ window.experimentVariables.experimentName = 'test-experiment';
await act(async () => render((
@@ -34,30 +53,6 @@ describe('ReviewRequirementsPanel', () => {
)));
- };
-
- afterEach(() => {
- cleanup();
- });
-
- it('routes to RequestCameraAccessPanel', async () => {
- await getPanel();
- const button = await screen.findByTestId('next-button');
- fireEvent.click(button);
- expect(history.location.pathname).toEqual('/request-camera-access');
- });
-
- it('updates optimizely experiment name in context', async () => {
- window.experimentVariables = {};
- window.experimentVariables.experimentName = 'test-experiment';
- await getPanel();
expect(context.setOptimizelyExperimentName).toHaveBeenCalledWith('test-experiment');
});
-
- it('displays an alert if the user\'s account information is managed by a third party', async () => {
- context.profileDataManager = 'test-org';
- await getPanel();
- const alert = await screen.getAllByText('test-org');
- expect(alert.length).toEqual(2);
- });
});
diff --git a/src/id-verification/tests/panels/SummaryPanel.test.jsx b/src/id-verification/tests/panels/SummaryPanel.test.jsx
index 0fae1a2..6b50b6a 100644
--- a/src/id-verification/tests/panels/SummaryPanel.test.jsx
+++ b/src/id-verification/tests/panels/SummaryPanel.test.jsx
@@ -76,13 +76,6 @@ describe('SummaryPanel', () => {
expect(uploadButton).toBeVisible();
});
- it('displays warning if account is managed by a third party', async () => {
- contextValue.profileDataManager = 'test-org';
- await getPanel();
- const warning = await screen.getAllByText('test-org');
- expect(warning.length).toEqual(2);
- });
-
it('submits', async () => {
const verificationData = {
facePhotoFile: contextValue.facePhotoFile,