feat: send nameOnAccount as full_name with all IDV submissions when learner has no name change and verified name feature is enabled
MST-1059: https://openedx.atlassian.net/browse/MST-1059 This code change changes how the full_name field is included in the form data sent during IDV submission. Before, full_name was only included in the form data when the learner entered a different name in the GetNameIdPanel than what was displayed to them as the default (i.e. their full name on their profile). If a full_name was provided in the request, the server would use the supplied full_name when creating the IDV record and would update the learner's full name on their profile accordingly. If a full_name was not provided in the request, then the server would fall back to the current full name on their profile when creating the IDV record and no change to the full name on their profile would occur. With the introduction of the Verified Name feature, things have changed. Assuming the feature is enabled, the name displayed to the learner is either the most recent pending or approved verified name or their full name on their profile if the former does not exist. This means that it is no longer correct for the server to create an IDV record using the current full name on the learner's profile when full_name is not provided, which, again, occurs if the learner does not change the name submitted with IDV. This is because, if the learner has a pending or approved verified name, that should be prioritzed over the full name on their profile. This code change sends the full_name field whether or not the learner has modified it in the IDV flow, if the verified name feature is enabled. This allows the server to create a verified name with whatever value is submitted to it through 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, as described above. 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. Their name may not change, but this will pollute the history by being logged as a requested change.
This commit is contained in:
@@ -11,6 +11,7 @@ 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';
|
||||
@@ -31,6 +32,7 @@ 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);
|
||||
@@ -72,6 +74,19 @@ function SummaryPanel(props) {
|
||||
};
|
||||
if (idPhotoName) {
|
||||
verificationData.idPhotoName = idPhotoName;
|
||||
} else if (verifiedNameEnabled) {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
verificationData.idPhotoName = nameOnAccount;
|
||||
}
|
||||
if (optimizelyExperimentName) {
|
||||
verificationData.optimizelyExperimentName = optimizelyExperimentName;
|
||||
|
||||
@@ -10,6 +10,7 @@ import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import * as dataService from '../../data/service';
|
||||
import IdVerificationContext from '../../IdVerificationContext';
|
||||
import SummaryPanel from '../../panels/SummaryPanel';
|
||||
import { VerifiedNameContext } from '../../VerifiedNameContext';
|
||||
|
||||
jest.mock('@edx/frontend-platform/analytics', () => ({
|
||||
sendTrackEvent: jest.fn(),
|
||||
@@ -27,7 +28,7 @@ describe('SummaryPanel', () => {
|
||||
intl: {},
|
||||
};
|
||||
|
||||
const contextValue = {
|
||||
const appContextValue = {
|
||||
facePhotoFile: 'test.jpg',
|
||||
idPhotoFile: 'test.jpg',
|
||||
nameOnAccount: 'test name',
|
||||
@@ -39,13 +40,19 @@ describe('SummaryPanel', () => {
|
||||
setReachedSummary: jest.fn(),
|
||||
};
|
||||
|
||||
const verifiedNameContextValue = {
|
||||
verifiedNameEnabled: false,
|
||||
};
|
||||
|
||||
const getPanel = async () => {
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlSummaryPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
<VerifiedNameContext.Provider value={verifiedNameContextValue}>
|
||||
<IdVerificationContext.Provider value={appContextValue}>
|
||||
<IntlSummaryPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</VerifiedNameContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
@@ -72,17 +79,17 @@ describe('SummaryPanel', () => {
|
||||
});
|
||||
|
||||
it('allows user to upload ID photo', async () => {
|
||||
contextValue.optimizelyExperimentName = '';
|
||||
appContextValue.optimizelyExperimentName = '';
|
||||
await getPanel();
|
||||
const collapsible = await screen.getAllByRole('button', { 'aria-expanded': false })[0];
|
||||
fireEvent.click(collapsible);
|
||||
const uploadButton = await screen.getByTestId('fileUpload');
|
||||
expect(uploadButton).toBeVisible();
|
||||
contextValue.optimizelyExperimentName = 'test-experiment';
|
||||
appContextValue.optimizelyExperimentName = 'test-experiment';
|
||||
});
|
||||
|
||||
it('displays warning if account is managed by a third party', async () => {
|
||||
contextValue.profileDataManager = 'test-org';
|
||||
appContextValue.profileDataManager = 'test-org';
|
||||
await getPanel();
|
||||
const warning = await screen.getAllByText('test-org');
|
||||
expect(warning.length).toEqual(1);
|
||||
@@ -90,29 +97,29 @@ describe('SummaryPanel', () => {
|
||||
|
||||
it('submits', async () => {
|
||||
const verificationData = {
|
||||
facePhotoFile: contextValue.facePhotoFile,
|
||||
idPhotoFile: contextValue.idPhotoFile,
|
||||
idPhotoName: contextValue.idPhotoName,
|
||||
optimizelyExperimentName: contextValue.optimizelyExperimentName,
|
||||
portraitPhotoMode: contextValue.portraitPhotoMode,
|
||||
idPhotoMode: contextValue.idPhotoMode,
|
||||
facePhotoFile: appContextValue.facePhotoFile,
|
||||
idPhotoFile: appContextValue.idPhotoFile,
|
||||
idPhotoName: appContextValue.idPhotoName,
|
||||
optimizelyExperimentName: appContextValue.optimizelyExperimentName,
|
||||
portraitPhotoMode: appContextValue.portraitPhotoMode,
|
||||
idPhotoMode: appContextValue.idPhotoMode,
|
||||
courseRunKey: null,
|
||||
};
|
||||
await getPanel();
|
||||
const button = await screen.findByTestId('submit-button');
|
||||
fireEvent.click(button);
|
||||
expect(dataService.submitIdVerification).toHaveBeenCalledWith(verificationData);
|
||||
await waitFor(() => expect(contextValue.stopUserMedia).toHaveBeenCalled());
|
||||
await waitFor(() => expect(appContextValue.stopUserMedia).toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it('does not submit a name if name is blank', async () => {
|
||||
contextValue.idPhotoName = '';
|
||||
appContextValue.idPhotoName = '';
|
||||
const verificationData = {
|
||||
facePhotoFile: contextValue.facePhotoFile,
|
||||
idPhotoFile: contextValue.idPhotoFile,
|
||||
portraitPhotoMode: contextValue.portraitPhotoMode,
|
||||
idPhotoMode: contextValue.idPhotoMode,
|
||||
optimizelyExperimentName: contextValue.optimizelyExperimentName,
|
||||
facePhotoFile: appContextValue.facePhotoFile,
|
||||
idPhotoFile: appContextValue.idPhotoFile,
|
||||
portraitPhotoMode: appContextValue.portraitPhotoMode,
|
||||
idPhotoMode: appContextValue.idPhotoMode,
|
||||
optimizelyExperimentName: appContextValue.optimizelyExperimentName,
|
||||
courseRunKey: null,
|
||||
};
|
||||
await getPanel();
|
||||
@@ -122,13 +129,13 @@ describe('SummaryPanel', () => {
|
||||
});
|
||||
|
||||
it('does not submit a name if name is unchanged', async () => {
|
||||
contextValue.idPhotoName = null;
|
||||
appContextValue.idPhotoName = null;
|
||||
const verificationData = {
|
||||
facePhotoFile: contextValue.facePhotoFile,
|
||||
idPhotoFile: contextValue.idPhotoFile,
|
||||
portraitPhotoMode: contextValue.portraitPhotoMode,
|
||||
idPhotoMode: contextValue.idPhotoMode,
|
||||
optimizelyExperimentName: contextValue.optimizelyExperimentName,
|
||||
facePhotoFile: appContextValue.facePhotoFile,
|
||||
idPhotoFile: appContextValue.idPhotoFile,
|
||||
portraitPhotoMode: appContextValue.portraitPhotoMode,
|
||||
idPhotoMode: appContextValue.idPhotoMode,
|
||||
optimizelyExperimentName: appContextValue.optimizelyExperimentName,
|
||||
courseRunKey: null,
|
||||
};
|
||||
await getPanel();
|
||||
@@ -137,6 +144,24 @@ describe('SummaryPanel', () => {
|
||||
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,
|
||||
portraitPhotoMode: appContextValue.portraitPhotoMode,
|
||||
idPhotoMode: appContextValue.idPhotoMode,
|
||||
optimizelyExperimentName: appContextValue.optimizelyExperimentName,
|
||||
courseRunKey: null,
|
||||
idPhotoName: appContextValue.nameOnAccount,
|
||||
};
|
||||
await getPanel();
|
||||
const button = await screen.findByTestId('submit-button');
|
||||
fireEvent.click(button);
|
||||
expect(dataService.submitIdVerification).toHaveBeenCalledWith(verificationData);
|
||||
});
|
||||
|
||||
it('shows error when cannot submit', async () => {
|
||||
dataService.submitIdVerification = jest.fn().mockReturnValue({ success: false });
|
||||
await getPanel();
|
||||
@@ -207,6 +232,6 @@ describe('SummaryPanel', () => {
|
||||
await getPanel();
|
||||
const collapsible = await screen.queryByTestId('collapsible');
|
||||
expect(collapsible).not.toBeInTheDocument();
|
||||
contextValue.optimizelyExperimentName = 'test-experiment';
|
||||
appContextValue.optimizelyExperimentName = 'test-experiment';
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user