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:
Michael Roytman
2021-11-01 12:55:52 -04:00
committed by GitHub
15 changed files with 52 additions and 441 deletions

View File

@@ -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: [],

View File

@@ -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));

View File

@@ -56,7 +56,6 @@ describe('NameChange', () => {
originalVerifiedName: 'edX Verified',
saveState: null,
useVerifiedNameForCerts: false,
verifiedNameEnabled: true,
intl: {},
};

View File

@@ -39,7 +39,6 @@ export const defaultState = {
verifiedName: null,
mostRecentVerifiedName: {},
verifiedNameHistory: {},
verifiedNameEnabled: false,
};
const reducer = (state = defaultState, action) => {

View File

@@ -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,
}),
);

View File

@@ -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();

View File

@@ -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',

View File

@@ -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)

View File

@@ -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,
};

View File

@@ -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,
};

View File

@@ -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>

View File

@@ -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}>

View File

@@ -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');
});
});

View File

@@ -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 () => {

View File

@@ -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,