added error messaging for image upload and idv submission

updated testing

wrapped button click

moved maximum file size to a const variable
This commit is contained in:
Alie Langston
2020-09-10 13:17:24 -04:00
parent 2dbccec1f1
commit b9efe6faee
5 changed files with 95 additions and 20 deletions

View File

@@ -35,7 +35,7 @@ function CameraHelpWithUpload(props) {
<p>
{props.intl.formatMessage(messages['id.verification.id.photo.instructions.upload'])}
</p>
<ImageFileUpload onFileChange={setAndTrackIdPhotoFile} />
<ImageFileUpload onFileChange={setAndTrackIdPhotoFile} intl={props.intl} />
</Collapsible>
</div>
);

View File

@@ -403,9 +403,14 @@ const messages = defineMessages({
},
'id.verification.id.photo.instructions.upload': {
id: 'id.verification.id.photo.instructions.upload',
defaultMessage: 'Please upload an ID photo. Ensure the entire ID fits inside the frame and is well-lit. (Supported formats: .jpg, .jpeg, .png)',
defaultMessage: 'Please upload an ID photo. Ensure the entire ID fits inside the frame and is well-lit. The file size must be under 10 MB. (Supported formats: .jpg, .jpeg, .png)',
description: 'Instructions for ID photo upload.',
},
'id.verification.id.photo.instructions.upload.error': {
id: 'id.verification.id.photo.instructions.upload.error',
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',
@@ -506,6 +511,11 @@ const messages = defineMessages({
defaultMessage: 'Submit',
description: 'Button to confirm all information is correct and submit.',
},
'id.verification.review.error': {
id: 'id.verification.review.error',
defaultMessage: 'edX Support Page',
description: 'Text linking to the support page.',
},
'id.verification.submitted.title': {
id: 'id.verification.submitted.title',
defaultMessage: 'Identity Verification in Progress',

View File

@@ -1,28 +1,52 @@
import React, { useCallback } from 'react';
import React, { useCallback, useState } from 'react';
import { intlShape } from '@edx/frontend-platform/i18n';
import PropTypes from 'prop-types';
import { Alert } from '@edx/paragon';
import messages from './IdVerification.messages';
export default function ImageFileUpload({ onFileChange, intl }) {
const [fileTooLargeError, setFileTooLargeError] = useState(false);
const maxFileSize = 10000000;
export default function ImageFileUpload({ onFileChange }) {
const handleChange = useCallback((e) => {
if (e.target.files.length === 0) {
return;
}
const fileObject = e.target.files[0];
const fileReader = new FileReader();
fileReader.addEventListener('load', () => onFileChange(fileReader.result));
fileReader.readAsDataURL(fileObject);
if (fileObject.size < maxFileSize) {
const fileReader = new FileReader();
fileReader.addEventListener('load', () => onFileChange(fileReader.result));
fileReader.readAsDataURL(fileObject);
} else {
setFileTooLargeError(true);
}
}, []);
return (
<input
type="file"
accept="image/*"
data-testid="fileUpload"
onChange={handleChange}
/>
<>
<input
type="file"
accept="image/*"
data-testid="fileUpload"
onChange={handleChange}
/>
{fileTooLargeError && (
<Alert
id="fileTooLargeError"
variant="danger"
tabIndex="-1"
style={{ marginTop: '1rem' }}
>
{intl.formatMessage(messages['id.verification.id.photo.instructions.upload.error'])}
</Alert>
)}
</>
);
}
ImageFileUpload.propTypes = {
onFileChange: PropTypes.func.isRequired,
intl: intlShape.isRequired,
};

View File

@@ -1,6 +1,6 @@
import React, { useState, useContext } from 'react';
import { history } from '@edx/frontend-platform';
import { Input, Button, Spinner } from '@edx/paragon';
import { Input, Button, Spinner, Alert } from '@edx/paragon';
import { Link } from 'react-router-dom';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
@@ -25,6 +25,7 @@ function SummaryPanel(props) {
} = useContext(IdVerificationContext);
const nameToBeUsed = idPhotoName || nameOnAccount || '';
const [isSubmitting, setIsSubmitting] = useState(false);
const [submissionError, setSubmissionError] = useState(false);
function SubmitButton() {
async function handleClick() {
@@ -39,6 +40,10 @@ function SummaryPanel(props) {
if (result.success) {
stopUserMedia();
history.push(nextPanelSlug);
} else {
stopUserMedia();
setIsSubmitting(false);
setSubmissionError(true);
}
}
return (
@@ -59,6 +64,24 @@ function SummaryPanel(props) {
name={panelSlug}
title={props.intl.formatMessage(messages['id.verification.review.title'])}
>
{submissionError &&
<Alert
variant="danger"
data-testid="submission-error"
dismissible
onClose={() => setSubmissionError(false)}
>
<FormattedMessage
id="idv.submission.alert.error"
defaultMessage={`
We encountered a technical error while trying to submit ID verification.
This might be a temporary issue, so please try again in a few minutes.
If the problem persists,
please go to {support_link} for help.
`}
values={{ support_link: <Alert.Link href="https://support.edx.org/hc/en-us">{props.intl.formatMessage(messages['id.verification.review.error'])}</Alert.Link> }}
/>
</Alert>}
<p>
{props.intl.formatMessage(messages['id.verification.review.description'])}
</p>

View File

@@ -5,7 +5,7 @@ import { render, cleanup, act, screen, fireEvent, waitFor } from '@testing-libra
import '@edx/frontend-platform/analytics';
import '@testing-library/jest-dom/extend-expect';
import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
import { submitIdVerification } from '../../data/service';
import * as dataService from '../../data/service';
import { IdVerificationContext } from '../../IdVerificationContext';
import SummaryPanel from '../../panels/SummaryPanel';
@@ -13,9 +13,8 @@ jest.mock('@edx/frontend-platform/analytics', () => ({
sendTrackEvent: jest.fn(),
}));
jest.mock('../../data/service', () => ({
submitIdVerification: jest.fn(() => ({ success: true, message: null })),
}));
jest.mock('../../data/service');
dataService.submitIdVerification = jest.fn().mockReturnValue({ success: true });
const IntlSummaryPanel = injectIntl(SummaryPanel);
@@ -74,7 +73,26 @@ describe('SummaryPanel', () => {
it('submits', async () => {
const button = await screen.findByTestId('submit-button');
fireEvent.click(button);
expect(submitIdVerification).toHaveBeenCalled();
await waitFor(() => expect(contextValue.stopUserMedia).toHaveBeenCalled())
expect(dataService.submitIdVerification).toHaveBeenCalled();
await waitFor(() => expect(contextValue.stopUserMedia).toHaveBeenCalled());
});
it('shows error when cannot submit', async () => {
await cleanup();
dataService.submitIdVerification = jest.fn().mockReturnValue({ success: false });
await act(async () => render((
<Router history={history}>
<IntlProvider locale="en">
<IdVerificationContext.Provider value={contextValue}>
<IntlSummaryPanel {...defaultProps} />
</IdVerificationContext.Provider>
</IntlProvider>
</Router>
)));
const button = await screen.findByTestId('submit-button');
await act(async () => fireEvent.click(button));
expect(dataService.submitIdVerification).toHaveBeenCalled();
const error = await screen.getByTestId('submission-error');
expect(error).toBeDefined();
});
});