Merge pull request #541 from edx/MST-1130-remove-verified-name-waffle-flag
feat: Remove Use of Verified Name Enabled Flag
This commit is contained in:
@@ -262,7 +262,6 @@ class AccountSettingsPage extends React.Component {
|
||||
renderFullNameHelpText = (status) => {
|
||||
if (
|
||||
!this.props.verifiedNameHistory
|
||||
|| !this.props.verifiedNameEnabled
|
||||
) {
|
||||
return this.props.intl.formatMessage(messages['account.settings.field.full.name.help.text']);
|
||||
}
|
||||
@@ -471,7 +470,7 @@ class AccountSettingsPage extends React.Component {
|
||||
// Show State field only if the country is US (could include Canada later)
|
||||
const showState = this.props.formValues.country === COUNTRY_WITH_STATES;
|
||||
|
||||
const { verifiedName, verifiedNameEnabled } = this.props;
|
||||
const { verifiedName } = this.props;
|
||||
|
||||
const timeZoneOptions = this.getLocalizedTimeZoneOptions(
|
||||
this.props.timeZoneOptions,
|
||||
@@ -484,7 +483,7 @@ class AccountSettingsPage extends React.Component {
|
||||
<>
|
||||
<div className="account-section" id="basic-information" ref={this.navLinkRefs['#basic-information']}>
|
||||
{
|
||||
verifiedNameEnabled && this.props.mostRecentVerifiedName
|
||||
this.props.mostRecentVerifiedName
|
||||
&& this.renderVerifiedNameMessage(this.props.mostRecentVerifiedName)
|
||||
}
|
||||
|
||||
@@ -512,8 +511,7 @@ class AccountSettingsPage extends React.Component {
|
||||
name="name"
|
||||
type="text"
|
||||
value={
|
||||
verifiedNameEnabled
|
||||
&& verifiedName?.status === 'submitted'
|
||||
verifiedName?.status === 'submitted'
|
||||
&& this.props.formValues.pending_name_change
|
||||
? this.props.formValues.pending_name_change
|
||||
: this.props.formValues.name
|
||||
@@ -525,22 +523,22 @@ class AccountSettingsPage extends React.Component {
|
||||
: this.renderEmptyStaticFieldMessage()
|
||||
}
|
||||
helpText={
|
||||
verifiedNameEnabled && verifiedName
|
||||
verifiedName
|
||||
? this.renderFullNameHelpText(verifiedName.status)
|
||||
: this.props.intl.formatMessage(messages['account.settings.field.full.name.help.text'])
|
||||
}
|
||||
isEditable={
|
||||
verifiedNameEnabled && verifiedName
|
||||
verifiedName
|
||||
? this.isEditable('verifiedName') && this.isEditable('name')
|
||||
: this.isEditable('name')
|
||||
}
|
||||
isGrayedOut={
|
||||
verifiedNameEnabled && verifiedName && !this.isEditable('verifiedName')
|
||||
verifiedName && !this.isEditable('verifiedName')
|
||||
}
|
||||
onChange={this.handleEditableFieldChange}
|
||||
onSubmit={this.handleSubmitProfileName}
|
||||
/>
|
||||
{verifiedNameEnabled && verifiedName
|
||||
{verifiedName
|
||||
&& (
|
||||
<EditableField
|
||||
name="verified_name"
|
||||
@@ -887,7 +885,6 @@ AccountSettingsPage.propTypes = {
|
||||
nameChangeModal: PropTypes.shape({
|
||||
formId: PropTypes.string,
|
||||
}),
|
||||
verifiedNameEnabled: PropTypes.bool,
|
||||
verifiedName: PropTypes.shape({
|
||||
verified_name: PropTypes.string,
|
||||
status: PropTypes.string,
|
||||
@@ -924,7 +921,6 @@ AccountSettingsPage.defaultProps = {
|
||||
isActive: true,
|
||||
secondary_email_enabled: false,
|
||||
nameChangeModal: {},
|
||||
verifiedNameEnabled: false,
|
||||
verifiedName: null,
|
||||
mostRecentVerifiedName: {},
|
||||
verifiedNameHistory: [],
|
||||
|
||||
@@ -28,9 +28,8 @@ function CertificatePreference({
|
||||
originalVerifiedName,
|
||||
saveState,
|
||||
useVerifiedNameForCerts,
|
||||
verifiedNameEnabled,
|
||||
}) {
|
||||
if (!verifiedNameEnabled || !originalVerifiedName) {
|
||||
if (!originalVerifiedName) {
|
||||
// If the user doesn't have an approved verified name, do not display this component
|
||||
return null;
|
||||
}
|
||||
@@ -161,7 +160,6 @@ CertificatePreference.propTypes = {
|
||||
originalVerifiedName: PropTypes.string,
|
||||
saveState: PropTypes.string,
|
||||
useVerifiedNameForCerts: PropTypes.bool,
|
||||
verifiedNameEnabled: PropTypes.bool,
|
||||
};
|
||||
|
||||
CertificatePreference.defaultProps = {
|
||||
@@ -169,7 +167,6 @@ CertificatePreference.defaultProps = {
|
||||
originalVerifiedName: '',
|
||||
saveState: null,
|
||||
useVerifiedNameForCerts: false,
|
||||
verifiedNameEnabled: false,
|
||||
};
|
||||
|
||||
export default connect(certPreferenceSelector)(injectIntl(CertificatePreference));
|
||||
|
||||
@@ -56,7 +56,6 @@ describe('NameChange', () => {
|
||||
originalVerifiedName: 'edX Verified',
|
||||
saveState: null,
|
||||
useVerifiedNameForCerts: false,
|
||||
verifiedNameEnabled: true,
|
||||
intl: {},
|
||||
};
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ export const defaultState = {
|
||||
verifiedName: null,
|
||||
mostRecentVerifiedName: {},
|
||||
verifiedNameHistory: {},
|
||||
verifiedNameEnabled: false,
|
||||
};
|
||||
|
||||
const reducer = (state = defaultState, action) => {
|
||||
|
||||
@@ -12,7 +12,6 @@ const verifiedNameSettingsSelector = createSelector(
|
||||
accountSettingsSelector,
|
||||
accountSettings => ({
|
||||
history: accountSettings.verifiedNameHistory.results,
|
||||
verifiedNameEnabled: accountSettings?.verifiedNameHistory.verified_name_enabled,
|
||||
useVerifiedNameForCerts: accountSettings?.verifiedNameHistory.use_verified_name_for_certs,
|
||||
}),
|
||||
);
|
||||
@@ -229,7 +228,6 @@ export const accountSettingsPageSelector = createSelector(
|
||||
mostRecentApprovedVerifiedNameValueSelector,
|
||||
mostRecentVerifiedNameSelector,
|
||||
sortedVerifiedNameHistorySelector,
|
||||
verifiedNameSettingsSelector,
|
||||
(
|
||||
accountSettings,
|
||||
siteLanguageOptions,
|
||||
@@ -247,7 +245,6 @@ export const accountSettingsPageSelector = createSelector(
|
||||
verifiedName,
|
||||
mostRecentVerifiedName,
|
||||
verifiedNameHistory,
|
||||
verifiedNameSettings,
|
||||
) => ({
|
||||
siteLanguageOptions,
|
||||
siteLanguage,
|
||||
@@ -268,19 +265,16 @@ export const accountSettingsPageSelector = createSelector(
|
||||
verifiedName,
|
||||
mostRecentVerifiedName,
|
||||
verifiedNameHistory,
|
||||
verifiedNameEnabled: verifiedNameSettings?.verifiedNameEnabled,
|
||||
}),
|
||||
);
|
||||
|
||||
export const certPreferenceSelector = createSelector(
|
||||
verifiedNameSettingsSelector,
|
||||
valuesSelector,
|
||||
formValuesSelector,
|
||||
mostRecentApprovedVerifiedNameValueSelector,
|
||||
saveStateSelector,
|
||||
errorSelector,
|
||||
(
|
||||
verifiedNameSettings,
|
||||
committedValues,
|
||||
formValues,
|
||||
mostRecentApprovedVerifiedNameValue,
|
||||
@@ -291,7 +285,6 @@ export const certPreferenceSelector = createSelector(
|
||||
originalVerifiedName: mostRecentApprovedVerifiedNameValue?.verified_name || '',
|
||||
useVerifiedNameForCerts: formValues.useVerifiedNameForCerts || false,
|
||||
saveState,
|
||||
verifiedNameEnabled: verifiedNameSettings.verifiedNameEnabled || false,
|
||||
formErrors: errors,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -177,19 +177,6 @@ export async function shouldDisplayDemographicsQuestions() {
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function getVerifiedNameEnabled() {
|
||||
let data;
|
||||
const client = getAuthenticatedHttpClient();
|
||||
try {
|
||||
const requestUrl = `${getConfig().LMS_BASE_URL}/api/edx_name_affirmation/v1/verified_name_enabled`;
|
||||
({ data } = await client.get(requestUrl));
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function getVerifiedName() {
|
||||
let data;
|
||||
const client = getAuthenticatedHttpClient();
|
||||
|
||||
@@ -521,21 +521,11 @@ const messages = defineMessages({
|
||||
defaultMessage: 'The file you have selected is too large. Please try again with a file less than 10MB.',
|
||||
description: 'Error message for file upload that is larger than 10MB.',
|
||||
},
|
||||
'id.verification.account.name.title': {
|
||||
id: 'id.verification.account.name.title',
|
||||
defaultMessage: 'Account Name Check',
|
||||
description: 'Title for the Account Name Check page.',
|
||||
},
|
||||
'id.verification.name.check.title': {
|
||||
id: 'id.verification.name.check.title',
|
||||
defaultMessage: 'Double-Check Your Name',
|
||||
description: 'Title for the page where a user double-checks that their name is correct.',
|
||||
},
|
||||
'id.verification.account.name.instructions': {
|
||||
id: 'id.verification.account.name.instructions',
|
||||
defaultMessage: 'The name on your account and the name on your ID must be an exact match. If not, please click "No" to update your account name.',
|
||||
description: 'Text to verify that the account name matches the name on the ID photo.',
|
||||
},
|
||||
'id.verification.name.check.instructions': {
|
||||
id: 'id.verification.name.check.instructions',
|
||||
defaultMessage: 'Does the name below match the name on your government-issued ID? If not, update the name below to match your goverment-issued ID.',
|
||||
@@ -546,41 +536,6 @@ const messages = defineMessages({
|
||||
defaultMessage: 'If the name below does not match your government-issued ID, your identity verification will be denied.',
|
||||
description: 'Text to inform the user that if the name displayed on the page does not match what is on their government-issued ID, identity verification will be denied.',
|
||||
},
|
||||
'id.verification.account.name.radio.label': {
|
||||
id: 'id.verification.account.name.radio.label',
|
||||
defaultMessage: 'Does the name on your ID match the Account Name below?',
|
||||
description: 'Question to ask the user whether their account name match the name on their ID card.',
|
||||
},
|
||||
'id.verification.name.check.radio.label': {
|
||||
id: 'id.verification.name.check.radio.label',
|
||||
defaultMessage: 'Select an option',
|
||||
description: 'Label for a radio button group where the user needs to choose one of two options.',
|
||||
},
|
||||
'id.verification.account.name.radio.yes': {
|
||||
id: 'id.verification.account.name.radio.yes',
|
||||
defaultMessage: 'Yes',
|
||||
description: 'The radio button that says the account name matches.',
|
||||
},
|
||||
'id.verification.name.check.radio.yes': {
|
||||
id: 'id.verification.name.check.radio.yes',
|
||||
defaultMessage: 'Yes, the name below matches my ID',
|
||||
description: 'Label for a radio button that indicates that the name displayed on the page matches the name on the user\'s ID.',
|
||||
},
|
||||
'id.verification.account.name.radio.no': {
|
||||
id: 'id.verification.account.name.radio.no',
|
||||
defaultMessage: 'No',
|
||||
description: 'The radio button that says the account name does not match.',
|
||||
},
|
||||
'id.verification.name.check.radio.no': {
|
||||
id: 'id.verification.name.check.radio.no',
|
||||
defaultMessage: 'No, the name below does not match my ID',
|
||||
description: 'Label for a radio button that indicates that the name displayed on the page does not match the name on the user\'s ID.',
|
||||
},
|
||||
'id.verification.account.name.error': {
|
||||
id: 'id.verification.account.name.error',
|
||||
defaultMessage: 'Please update account name to match the name on your ID.',
|
||||
description: 'Error that shows when the user needs to update their account name to match the name on their ID.',
|
||||
},
|
||||
'id.verification.name.error': {
|
||||
id: 'id.verification.name.error',
|
||||
defaultMessage: 'Please enter your name as it appears on your government-issued ID.',
|
||||
@@ -596,11 +551,6 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Account Settings',
|
||||
description: 'Link to Account Settings.',
|
||||
},
|
||||
'id.verification.account.name.label': {
|
||||
id: 'id.verification.account.name.label',
|
||||
defaultMessage: 'Account Name',
|
||||
description: 'Label for account name input.',
|
||||
},
|
||||
'id.verification.name.label': {
|
||||
id: 'id.verification.name.label',
|
||||
defaultMessage: 'Name',
|
||||
@@ -611,11 +561,6 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Photo of your ID to be submitted.',
|
||||
description: 'Alt text for the photo of the user\'s ID.',
|
||||
},
|
||||
'id.verification.account.name.save': {
|
||||
id: 'id.verification.account.name.save',
|
||||
defaultMessage: 'Save and Next',
|
||||
description: 'Button to save the account name.',
|
||||
},
|
||||
'id.verification.review.title': {
|
||||
id: 'id.verification.review.title',
|
||||
defaultMessage: 'Review Your Photos',
|
||||
|
||||
@@ -15,7 +15,7 @@ import { VerifiedNameContext } from './VerifiedNameContext';
|
||||
|
||||
export default function IdVerificationContextProvider({ children }) {
|
||||
const { authenticatedUser } = useContext(AppContext);
|
||||
const { verifiedNameHistoryCallStatus, verifiedName, verifiedNameEnabled } = useContext(VerifiedNameContext);
|
||||
const { verifiedNameHistoryCallStatus, verifiedName } = useContext(VerifiedNameContext);
|
||||
|
||||
const idVerificationData = useAsyncCall(getExistingIdVerification);
|
||||
const enrollmentsData = useAsyncCall(getEnrollments);
|
||||
@@ -64,20 +64,6 @@ export default function IdVerificationContextProvider({ children }) {
|
||||
existingIdVerification = idVerificationData.data;
|
||||
}
|
||||
|
||||
if (verifiedNameHistoryCallStatus === SUCCESS_STATUS && idVerificationData.status === SUCCESS_STATUS) {
|
||||
// With verified name we can redo verification multiple times
|
||||
// if not a successful request prevents re-verification
|
||||
if (!verifiedNameEnabled && existingIdVerification && !existingIdVerification.canVerify) {
|
||||
const { status } = existingIdVerification;
|
||||
canVerify = false;
|
||||
if (status === 'pending' || status === 'approved') {
|
||||
error = ERROR_REASONS.EXISTING_REQUEST;
|
||||
} else {
|
||||
error = ERROR_REASONS.CANNOT_VERIFY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (enrollmentsData.status === SUCCESS_STATUS && enrollmentsData?.data) {
|
||||
const verifiedEnrollments = enrollmentsData.data.filter((enrollment) => (
|
||||
VERIFIED_MODES.includes(enrollment.mode)
|
||||
|
||||
@@ -11,22 +11,15 @@ export const VerifiedNameContext = createContext();
|
||||
export function VerifiedNameContextProvider({ children }) {
|
||||
const verifiedNameHistoryData = useAsyncCall(getVerifiedNameHistory);
|
||||
|
||||
let verifiedNameEnabled = false;
|
||||
let verifiedName = '';
|
||||
const { status, data } = verifiedNameHistoryData;
|
||||
if (status === SUCCESS_STATUS && data) {
|
||||
const { verified_name_enabled: verifiedNameFeatureEnabled, results } = data;
|
||||
verifiedNameEnabled = verifiedNameFeatureEnabled;
|
||||
|
||||
if (verifiedNameFeatureEnabled) {
|
||||
const applicableVerifiedName = getMostRecentApprovedOrPendingVerifiedName(results);
|
||||
verifiedName = applicableVerifiedName;
|
||||
}
|
||||
const { results } = data;
|
||||
verifiedName = getMostRecentApprovedOrPendingVerifiedName(results);
|
||||
}
|
||||
|
||||
const value = {
|
||||
verifiedNameHistoryCallStatus: status,
|
||||
verifiedNameEnabled,
|
||||
verifiedName,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
import React, {
|
||||
useContext, useState, useEffect, useRef,
|
||||
useContext, 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';
|
||||
import IdVerificationContext from '../IdVerificationContext';
|
||||
import { VerifiedNameContext } from '../VerifiedNameContext';
|
||||
|
||||
import messages from '../IdVerification.messages';
|
||||
|
||||
function GetNameIdPanelVerified(props) {
|
||||
function GetNameIdPanel(props) {
|
||||
const { push, location } = useHistory();
|
||||
const nameInputRef = useRef();
|
||||
const panelSlug = 'get-name-id';
|
||||
@@ -94,180 +91,7 @@ function GetNameIdPanelVerified(props) {
|
||||
);
|
||||
}
|
||||
|
||||
function GetNameIdPanelNonVerified(props) {
|
||||
const { push } = useHistory();
|
||||
const panelSlug = 'get-name-id';
|
||||
const [nameMatches, setNameMatches] = useState(true);
|
||||
const nameInputRef = useRef();
|
||||
const nextPanelSlug = useNextPanelSlug(panelSlug);
|
||||
|
||||
const {
|
||||
nameOnAccount, userId, profileDataManager, idPhotoName, setIdPhotoName,
|
||||
} = useContext(IdVerificationContext);
|
||||
const nameOnAccountValue = nameOnAccount || '';
|
||||
const invalidName = !nameMatches && (!idPhotoName || idPhotoName === nameOnAccount);
|
||||
const blankName = !nameOnAccount && !idPhotoName;
|
||||
|
||||
useEffect(() => {
|
||||
setIdPhotoName(null);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!nameMatches && nameInputRef.current) {
|
||||
nameInputRef.current.focus();
|
||||
}
|
||||
if (!nameMatches) {
|
||||
sendTrackEvent('edx.id_verification.name_change', {
|
||||
category: 'id_verification',
|
||||
user_id: userId,
|
||||
});
|
||||
}
|
||||
if (blankName) {
|
||||
setNameMatches(false);
|
||||
}
|
||||
}, [nameMatches, blankName]);
|
||||
|
||||
function getNameValue() {
|
||||
if (!nameMatches) {
|
||||
// Explicitly check for null, as an empty string should still be used here
|
||||
if (idPhotoName === null) {
|
||||
return nameOnAccountValue;
|
||||
}
|
||||
return idPhotoName;
|
||||
}
|
||||
return nameOnAccountValue;
|
||||
}
|
||||
|
||||
function getErrorMessage() {
|
||||
if (profileDataManager) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="id.verification.account.name.managed.alert"
|
||||
defaultMessage="Your profile settings are managed by {managerTitle}, so you are not allowed to update your name. Please contact your {profileDataManager} administrator or {support} for help."
|
||||
description="Alert message informing the user their account name is managed by a third party."
|
||||
values={{
|
||||
managerTitle: <strong>{profileDataManager}</strong>,
|
||||
profileDataManager,
|
||||
support: (
|
||||
<Hyperlink destination={getConfig().SUPPORT_URL} target="_blank">
|
||||
{props.intl.formatMessage(messages['id.verification.support'])}
|
||||
</Hyperlink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return props.intl.formatMessage(messages['id.verification.account.name.error']);
|
||||
}
|
||||
|
||||
function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
// If the input is empty, or if no changes have been made to the
|
||||
// mismatching name, the user should not be able to proceed.
|
||||
if (!invalidName && !blankName) {
|
||||
push(nextPanelSlug);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<BasePanel
|
||||
name={panelSlug}
|
||||
title={props.intl.formatMessage(messages['id.verification.account.name.title'])}
|
||||
>
|
||||
<p>
|
||||
{props.intl.formatMessage(messages['id.verification.account.name.instructions'])}
|
||||
</p>
|
||||
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<Form.Group>
|
||||
<Form.Label className="font-weight-bold" htmlFor="nameMatchesYes">
|
||||
{props.intl.formatMessage(messages['id.verification.account.name.radio.label'])}
|
||||
</Form.Label>
|
||||
<Form.Check
|
||||
type="radio"
|
||||
id="nameMatchesYes"
|
||||
name="nameMatches"
|
||||
data-testid="name-matches-yes"
|
||||
label={props.intl.formatMessage(messages['id.verification.account.name.radio.yes'])}
|
||||
checked={nameMatches}
|
||||
disabled={!nameOnAccount}
|
||||
onChange={() => {
|
||||
setNameMatches(true);
|
||||
setIdPhotoName(null);
|
||||
}}
|
||||
/>
|
||||
<Form.Check
|
||||
type="radio"
|
||||
id="nameMatchesNo"
|
||||
name="nameMatches"
|
||||
data-testid="name-matches-no"
|
||||
label={props.intl.formatMessage(messages['id.verification.account.name.radio.no'])}
|
||||
checked={!nameMatches}
|
||||
disabled={!nameOnAccount}
|
||||
onChange={() => setNameMatches(false)}
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group>
|
||||
<Form.Label className="font-weight-bold" htmlFor="photo-id-name">
|
||||
{props.intl.formatMessage(messages['id.verification.account.name.label'])}
|
||||
</Form.Label>
|
||||
<Form.Control
|
||||
controlId="photo-id-name"
|
||||
size="lg"
|
||||
type="text"
|
||||
ref={nameInputRef}
|
||||
readOnly={nameMatches || !!profileDataManager}
|
||||
isInvalid={invalidName || blankName}
|
||||
aria-describedby="photo-id-name-feedback"
|
||||
value={getNameValue()}
|
||||
onChange={e => setIdPhotoName(e.target.value)}
|
||||
data-testid="name-input"
|
||||
/>
|
||||
{(invalidName || !!profileDataManager) && (
|
||||
<Form.Control.Feedback
|
||||
id="photo-id-name-feedback"
|
||||
data-testid="id-name-feedback-message"
|
||||
type="invalid"
|
||||
>
|
||||
{getErrorMessage()}
|
||||
</Form.Control.Feedback>
|
||||
)}
|
||||
</Form.Group>
|
||||
</Form>
|
||||
|
||||
<div className="action-row">
|
||||
<Link
|
||||
to={nextPanelSlug}
|
||||
className={`btn btn-primary ${(invalidName || blankName) && 'disabled'}`}
|
||||
data-testid="next-button"
|
||||
aria-disabled={invalidName || blankName}
|
||||
>
|
||||
{
|
||||
!nameMatches
|
||||
? props.intl.formatMessage(messages['id.verification.account.name.save'])
|
||||
: props.intl.formatMessage(messages['id.verification.next'])
|
||||
}
|
||||
</Link>
|
||||
</div>
|
||||
</BasePanel>
|
||||
);
|
||||
}
|
||||
|
||||
function GetNameIdPanel(props) {
|
||||
const { verifiedNameEnabled } = useContext(VerifiedNameContext);
|
||||
|
||||
if (verifiedNameEnabled) {
|
||||
return <GetNameIdPanelVerified {...props} />;
|
||||
}
|
||||
|
||||
return <GetNameIdPanelNonVerified {...props} />;
|
||||
}
|
||||
|
||||
GetNameIdPanelVerified.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
GetNameIdPanelNonVerified.propTypes = {
|
||||
GetNameIdPanel.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import { useNextPanelSlug } from '../routing-utilities';
|
||||
import BasePanel from './BasePanel';
|
||||
import IdVerificationContext from '../IdVerificationContext';
|
||||
import ImagePreview from '../ImagePreview';
|
||||
import { VerifiedNameContext } from '../VerifiedNameContext';
|
||||
|
||||
import messages from '../IdVerification.messages';
|
||||
import CameraHelpWithUpload from '../CameraHelpWithUpload';
|
||||
@@ -32,7 +31,6 @@ function SummaryPanel(props) {
|
||||
portraitPhotoMode,
|
||||
idPhotoMode,
|
||||
} = useContext(IdVerificationContext);
|
||||
const { verifiedNameEnabled } = useContext(VerifiedNameContext);
|
||||
const nameToBeUsed = idPhotoName || nameOnAccount || '';
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [submissionError, setSubmissionError] = useState(null);
|
||||
@@ -74,17 +72,10 @@ function SummaryPanel(props) {
|
||||
};
|
||||
if (idPhotoName) {
|
||||
verificationData.idPhotoName = idPhotoName;
|
||||
} else if (verifiedNameEnabled) {
|
||||
} else {
|
||||
/**
|
||||
* If learner has not entered an idPhotoName on the GetNameIdPanel,
|
||||
* and the verified name feature is enabled, use the current nameOnAccount
|
||||
* when submitting IDV. The reason we only do this if the feature is enabled
|
||||
* is that, when the feature is off, the server will change the learner's
|
||||
* profile name to this value. If we send the idPhotoName on all requests,
|
||||
* even ones where the learner does not change the idPhotoName, then the
|
||||
* server will record that the full name on the learner's profile has
|
||||
* a requested change, even if the name is the same. This will pollute
|
||||
* the history.
|
||||
* use the current nameOnAccount when submitting IDV.
|
||||
*/
|
||||
verificationData.idPhotoName = nameOnAccount;
|
||||
}
|
||||
@@ -220,9 +211,7 @@ function SummaryPanel(props) {
|
||||
{!optimizelyExperimentName && <CameraHelpWithUpload />}
|
||||
<div className="form-group">
|
||||
<label htmlFor="name-to-be-used" className="font-weight-bold">
|
||||
{verifiedNameEnabled
|
||||
? props.intl.formatMessage(messages['id.verification.name.label'])
|
||||
: props.intl.formatMessage(messages['id.verification.account.name.label'])}
|
||||
{props.intl.formatMessage(messages['id.verification.name.label'])}
|
||||
</label>
|
||||
{renderManagedProfileMessage()}
|
||||
<div className="d-flex">
|
||||
@@ -242,29 +231,14 @@ function SummaryPanel(props) {
|
||||
state: { fromSummary: true },
|
||||
}}
|
||||
>
|
||||
{
|
||||
verifiedNameEnabled
|
||||
? (
|
||||
<FormattedMessage
|
||||
id="id.verification.account.name.edit"
|
||||
defaultMessage="Edit {sr}"
|
||||
description="Button to edit account name, with clarifying information for screen readers."
|
||||
values={{
|
||||
sr: <span className="sr-only">Name</span>,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<FormattedMessage
|
||||
id="id.verification.account.name.edit"
|
||||
defaultMessage="Edit {sr}"
|
||||
description="Button to edit account name, with clarifying information for screen readers."
|
||||
values={{
|
||||
sr: <span className="sr-only">Account Name</span>,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
<FormattedMessage
|
||||
id="id.verification.account.name.edit"
|
||||
defaultMessage="Edit {sr}"
|
||||
description="Button to edit name, with clarifying information for screen readers."
|
||||
values={{
|
||||
sr: <span className="sr-only">Name</span>,
|
||||
}}
|
||||
/>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -32,7 +32,7 @@ describe('IdVerificationContextProvider', () => {
|
||||
|
||||
it('renders correctly and calls getExistingIdVerification + getEnrollments', async () => {
|
||||
const appContext = { authenticatedUser: { userId: 3, roles: [] } };
|
||||
const verifiedNameContext = { verifiedName: '', verifiedNameEnabled: false };
|
||||
const verifiedNameContext = { verifiedName: '' };
|
||||
await act(async () => render((
|
||||
<AppContext.Provider value={appContext}>
|
||||
<VerifiedNameContext.Provider value={verifiedNameContext}>
|
||||
@@ -54,7 +54,7 @@ describe('IdVerificationContextProvider', () => {
|
||||
roles: ['enterprise_learner'],
|
||||
},
|
||||
};
|
||||
const verifiedNameContext = { verifiedName: '', verifiedNameEnabled: false };
|
||||
const verifiedNameContext = { verifiedName: '' };
|
||||
await act(async () => render((
|
||||
<AppContext.Provider value={appContext}>
|
||||
<VerifiedNameContext.Provider value={verifiedNameContext}>
|
||||
|
||||
@@ -5,11 +5,10 @@ import { getVerifiedNameHistory } from '../../account-settings/data/service';
|
||||
import { VerifiedNameContext, VerifiedNameContextProvider } from '../VerifiedNameContext';
|
||||
|
||||
const VerifiedNameContextTestComponent = () => {
|
||||
const { verifiedName, verifiedNameEnabled } = useContext(VerifiedNameContext);
|
||||
const { verifiedName } = useContext(VerifiedNameContext);
|
||||
return (
|
||||
<>
|
||||
{verifiedNameEnabled && (<div data-testid="verified-name">{verifiedName}</div>)}
|
||||
<div data-testid="verified-name-enabled">{verifiedNameEnabled ? 'true' : 'false'}</div>
|
||||
{verifiedName && (<div data-testid="verified-name">{verifiedName}</div>)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -38,9 +37,8 @@ describe('VerifiedNameContextProvider', () => {
|
||||
await waitFor(() => expect(getVerifiedNameHistory).toHaveBeenCalledTimes(1));
|
||||
});
|
||||
|
||||
it('sets verifiedName and verifiedNameEnabled correctly when verified name feature enabled', async () => {
|
||||
it('sets verifiedName', async () => {
|
||||
const mockReturnValue = {
|
||||
verified_name_enabled: true,
|
||||
results: [{
|
||||
verified_name: 'Michael',
|
||||
status: 'approved',
|
||||
@@ -57,28 +55,5 @@ describe('VerifiedNameContextProvider', () => {
|
||||
|
||||
await waitFor(() => expect(getVerifiedNameHistory).toHaveBeenCalledTimes(1));
|
||||
expect(getByTestId('verified-name')).toHaveTextContent('Michael');
|
||||
expect(getByTestId('verified-name-enabled')).toHaveTextContent('true');
|
||||
});
|
||||
|
||||
it('sets verifiedName and verifiedNameEnabled correctly when verified name feature not enabled', async () => {
|
||||
const mockReturnValue = {
|
||||
verified_name_enabled: false,
|
||||
results: [{
|
||||
verified_name: 'Michael',
|
||||
status: 'approved',
|
||||
created: '2021-08-31T18:33:32.489200Z',
|
||||
}],
|
||||
};
|
||||
getVerifiedNameHistory.mockReturnValueOnce(mockReturnValue);
|
||||
|
||||
const { queryByTestId } = render((
|
||||
<VerifiedNameContextProvider {...defaultProps}>
|
||||
<VerifiedNameContextTestComponent />
|
||||
</VerifiedNameContextProvider>
|
||||
));
|
||||
|
||||
await waitFor(() => expect(getVerifiedNameHistory).toHaveBeenCalledTimes(1));
|
||||
expect(queryByTestId('verified-name')).toBeNull();
|
||||
expect(queryByTestId('verified-name-enabled')).toHaveTextContent('false');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -32,16 +32,14 @@ describe('GetNameIdPanel', () => {
|
||||
idPhotoFile: 'test.jpg',
|
||||
};
|
||||
|
||||
const verifiedNameContextValue = {
|
||||
verifiedNameEnabled: false,
|
||||
};
|
||||
const verifiedNameContextValue = {};
|
||||
|
||||
const getPanel = async () => {
|
||||
const getPanel = async (idVerificationContextValue = IDVerificationContextValue) => {
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<VerifiedNameContext.Provider value={verifiedNameContextValue}>
|
||||
<IdVerificationContext.Provider value={IDVerificationContextValue}>
|
||||
<IdVerificationContext.Provider value={idVerificationContextValue}>
|
||||
<IntlGetNameIdPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</VerifiedNameContext.Provider>
|
||||
@@ -54,65 +52,28 @@ describe('GetNameIdPanel', () => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('edits', async () => {
|
||||
it('shows feedback message when user has an empty name', 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');
|
||||
const errorMessageQuery = await screen.queryByTestId('id-name-feedback-message');
|
||||
|
||||
expect(input).toHaveAttribute('readonly');
|
||||
expect(errorMessageQuery).toBeNull();
|
||||
|
||||
fireEvent.click(noButton);
|
||||
expect(input).not.toHaveAttribute('readonly');
|
||||
expect(nextButton.classList.contains('disabled')).toBe(true);
|
||||
expect(nextButton).toHaveAttribute('aria-disabled');
|
||||
|
||||
fireEvent.change(input, { target: { value: 'test change' } });
|
||||
expect(IDVerificationContextValue.setIdPhotoName).toHaveBeenCalled();
|
||||
// Ensure the feedback message on name shows when the user says the name does not match ID
|
||||
// Ensure the feedback message on name shows when the user has an empty name
|
||||
expect(await screen.queryByTestId('id-name-feedback-message')).toBeTruthy();
|
||||
|
||||
fireEvent.click(yesButton);
|
||||
expect(input).toHaveAttribute('readonly');
|
||||
expect(IDVerificationContextValue.setIdPhotoName).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('disables radio buttons + next button and enables input if account name is blank', async () => {
|
||||
IDVerificationContextValue.nameOnAccount = '';
|
||||
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');
|
||||
const errorMessageQuery = await screen.queryByTestId('id-name-feedback-message');
|
||||
|
||||
expect(yesButton).toBeDisabled();
|
||||
expect(noButton).toBeDisabled();
|
||||
expect(input).not.toHaveAttribute('readonly');
|
||||
expect(nextButton.classList.contains('disabled')).toBe(true);
|
||||
expect(nextButton).toHaveAttribute('aria-disabled');
|
||||
expect(errorMessageQuery).toBeTruthy();
|
||||
it('does not show feedback message when user has an non-empty name', async () => {
|
||||
const idVerificationContextValue = {
|
||||
...IDVerificationContextValue,
|
||||
idPhotoName: 'test',
|
||||
};
|
||||
await getPanel(idVerificationContextValue);
|
||||
// Ensure the feedback message on name shows when the user has an empty name
|
||||
expect(await screen.queryByTestId('id-name-feedback-message')).toBeNull();
|
||||
});
|
||||
|
||||
it('blocks the user from changing account name if managed by a third party', async () => {
|
||||
IDVerificationContextValue.profileDataManager = 'test-org';
|
||||
it('calls setIdPhotoName with correct name', async () => {
|
||||
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).toHaveAttribute('readonly');
|
||||
expect(nextButton.classList.contains('disabled')).toBe(true);
|
||||
expect(nextButton).toHaveAttribute('aria-disabled');
|
||||
const warning = await screen.getAllByText('test-org');
|
||||
expect(warning.length).toEqual(1);
|
||||
fireEvent.change(input, { target: { value: 'test' } });
|
||||
expect(IDVerificationContextValue.setIdPhotoName).toHaveBeenCalledWith('test');
|
||||
});
|
||||
|
||||
it('routes to SummaryPanel', async () => {
|
||||
|
||||
@@ -39,9 +39,7 @@ describe('SummaryPanel', () => {
|
||||
setReachedSummary: jest.fn(),
|
||||
};
|
||||
|
||||
const verifiedNameContextValue = {
|
||||
verifiedNameEnabled: false,
|
||||
};
|
||||
const verifiedNameContextValue = {};
|
||||
|
||||
const getPanel = async () => {
|
||||
await act(async () => render((
|
||||
@@ -111,7 +109,7 @@ describe('SummaryPanel', () => {
|
||||
await waitFor(() => expect(appContextValue.stopUserMedia).toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it('does not submit a name if name is blank', async () => {
|
||||
it('submits a name if name is blank', async () => {
|
||||
appContextValue.idPhotoName = '';
|
||||
const verificationData = {
|
||||
facePhotoFile: appContextValue.facePhotoFile,
|
||||
@@ -120,6 +118,7 @@ describe('SummaryPanel', () => {
|
||||
idPhotoMode: appContextValue.idPhotoMode,
|
||||
optimizelyExperimentName: appContextValue.optimizelyExperimentName,
|
||||
courseRunKey: null,
|
||||
idPhotoName: appContextValue.nameOnAccount,
|
||||
};
|
||||
await getPanel();
|
||||
const button = await screen.findByTestId('submit-button');
|
||||
@@ -127,25 +126,8 @@ describe('SummaryPanel', () => {
|
||||
expect(dataService.submitIdVerification).toHaveBeenCalledWith(verificationData);
|
||||
});
|
||||
|
||||
it('does not submit a name if name is unchanged', async () => {
|
||||
it('submits a name if a name is unchanged', async () => {
|
||||
appContextValue.idPhotoName = null;
|
||||
const verificationData = {
|
||||
facePhotoFile: appContextValue.facePhotoFile,
|
||||
idPhotoFile: appContextValue.idPhotoFile,
|
||||
portraitPhotoMode: appContextValue.portraitPhotoMode,
|
||||
idPhotoMode: appContextValue.idPhotoMode,
|
||||
optimizelyExperimentName: appContextValue.optimizelyExperimentName,
|
||||
courseRunKey: null,
|
||||
};
|
||||
await getPanel();
|
||||
const button = await screen.findByTestId('submit-button');
|
||||
fireEvent.click(button);
|
||||
expect(dataService.submitIdVerification).toHaveBeenCalledWith(verificationData);
|
||||
});
|
||||
|
||||
it('submits a name if a name is unchanged if verified name feature is enabled', async () => {
|
||||
appContextValue.idPhotoName = null;
|
||||
verifiedNameContextValue.verifiedNameEnabled = true;
|
||||
const verificationData = {
|
||||
facePhotoFile: appContextValue.facePhotoFile,
|
||||
idPhotoFile: appContextValue.idPhotoFile,
|
||||
|
||||
Reference in New Issue
Block a user