feat: remove unused IDV experiment paths
This commit is contained in:
@@ -262,7 +262,6 @@ class Camera extends React.Component {
|
||||
const dataUri = this.cameraPhoto.getDataUri(config);
|
||||
this.setState({ dataUri });
|
||||
this.props.onImageCapture(dataUri);
|
||||
this.props.setPhotoMode('camera');
|
||||
}
|
||||
|
||||
playShutterClick() {
|
||||
@@ -360,7 +359,6 @@ class Camera extends React.Component {
|
||||
Camera.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
onImageCapture: PropTypes.func.isRequired,
|
||||
setPhotoMode: PropTypes.func.isRequired,
|
||||
isPortrait: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,30 +1,14 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Collapsible } from '@edx/paragon';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import messages from './IdVerification.messages';
|
||||
import IdVerificationContext from './IdVerificationContext';
|
||||
|
||||
function CameraHelp(props) {
|
||||
const { optimizelyExperimentName } = useContext(IdVerificationContext);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ optimizelyExperimentName
|
||||
&& (
|
||||
<Collapsible
|
||||
styling="card"
|
||||
title={props.intl.formatMessage(messages['id.verification.camera.help.upload.question'])}
|
||||
className="mb-4 shadow"
|
||||
defaultOpen={props.isOpen}
|
||||
>
|
||||
<p>
|
||||
{props.intl.formatMessage(messages['id.verification.camera.help.upload.answer'])}
|
||||
</p>
|
||||
</Collapsible>
|
||||
)}
|
||||
<Collapsible
|
||||
styling="card"
|
||||
title={props.intl.formatMessage(messages['id.verification.camera.help.sight.question'])}
|
||||
|
||||
@@ -2,72 +2,55 @@ import React, { useContext } from 'react';
|
||||
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Button, Collapsible } from '@edx/paragon';
|
||||
import { Link } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import IdVerificationContext, { MEDIA_ACCESS } from './IdVerificationContext';
|
||||
import IdVerificationContext from './IdVerificationContext';
|
||||
import messages from './IdVerification.messages';
|
||||
|
||||
function CollapsibleImageHelp(props) {
|
||||
const {
|
||||
userId, shouldUseCamera, setShouldUseCamera, optimizelyExperimentName, mediaAccess,
|
||||
userId, useCameraForId, setUseCameraForId,
|
||||
} = useContext(IdVerificationContext);
|
||||
|
||||
function handleClick() {
|
||||
const toggleTo = shouldUseCamera ? 'upload' : 'camera';
|
||||
const toggleTo = useCameraForId ? 'upload' : 'camera';
|
||||
const eventName = `edx.id_verification.toggle_to.${toggleTo}`;
|
||||
sendTrackEvent(eventName, {
|
||||
category: 'id_verification',
|
||||
user_id: userId,
|
||||
});
|
||||
setShouldUseCamera(!shouldUseCamera);
|
||||
setUseCameraForId(!useCameraForId);
|
||||
}
|
||||
|
||||
if (optimizelyExperimentName && mediaAccess !== MEDIA_ACCESS.DENIED && mediaAccess !== MEDIA_ACCESS.UNSUPPORTED) {
|
||||
return (
|
||||
<Collapsible
|
||||
styling="card"
|
||||
title={shouldUseCamera ? props.intl.formatMessage(messages['id.verification.photo.upload.help.title']) : props.intl.formatMessage(messages['id.verification.photo.camera.help.title'])}
|
||||
className="mb-4 shadow"
|
||||
defaultOpen
|
||||
return (
|
||||
<Collapsible
|
||||
styling="card"
|
||||
title={useCameraForId
|
||||
? props.intl.formatMessage(messages['id.verification.photo.upload.help.title'])
|
||||
: props.intl.formatMessage(messages['id.verification.photo.camera.help.title'])}
|
||||
className="mb-4 shadow"
|
||||
defaultOpen
|
||||
>
|
||||
<p data-testid="help-text">
|
||||
{useCameraForId
|
||||
? props.intl.formatMessage(messages['id.verification.photo.upload.help.text'])
|
||||
: props.intl.formatMessage(messages['id.verification.photo.camera.help.text'])}
|
||||
</p>
|
||||
<Button
|
||||
title={useCameraForId ? 'Upload Photo' : 'Take Photo'} // TO-DO: translation
|
||||
data-testid="toggle-button"
|
||||
onClick={handleClick}
|
||||
style={{ marginTop: '0.5rem' }}
|
||||
>
|
||||
<p data-testid="help-text">
|
||||
{shouldUseCamera
|
||||
? props.intl.formatMessage(messages['id.verification.photo.upload.help.text'])
|
||||
: props.intl.formatMessage(messages['id.verification.photo.camera.help.text'])}
|
||||
</p>
|
||||
{ (mediaAccess === MEDIA_ACCESS.PENDING && !shouldUseCamera)
|
||||
? (
|
||||
// if a user has not enabled camera access yet, and they are trying to switch
|
||||
// to camera mode, direct them to panel that requests camera access
|
||||
<Link
|
||||
to={{ pathname: 'request-camera-access', state: { fromPortraitCapture: props.isPortrait, fromIdCapture: !props.isPortrait } }}
|
||||
className="btn btn-primary"
|
||||
data-testid="access-link"
|
||||
>
|
||||
{props.intl.formatMessage(messages['id.verification.photo.camera.help.button'])}
|
||||
</Link>
|
||||
)
|
||||
: (
|
||||
<Button
|
||||
title={shouldUseCamera ? 'Upload Portrait Photo' : 'Take Portrait Photo'}
|
||||
data-testid="toggle-button"
|
||||
onClick={handleClick}
|
||||
style={{ marginTop: '0.5rem' }}
|
||||
>
|
||||
{shouldUseCamera ? props.intl.formatMessage(messages['id.verification.photo.upload.help.button']) : props.intl.formatMessage(messages['id.verification.photo.camera.help.button'])}
|
||||
</Button>
|
||||
)}
|
||||
</Collapsible>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
{useCameraForId
|
||||
? props.intl.formatMessage(messages['id.verification.photo.upload.help.button'])
|
||||
: props.intl.formatMessage(messages['id.verification.photo.camera.help.button'])}
|
||||
</Button>
|
||||
</Collapsible>
|
||||
);
|
||||
}
|
||||
|
||||
CollapsibleImageHelp.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
isPortrait: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(CollapsibleImageHelp);
|
||||
|
||||
@@ -11,11 +11,6 @@ const messages = defineMessages({
|
||||
defaultMessage: 'support',
|
||||
description: 'Website support.',
|
||||
},
|
||||
'id.verification.continue.upload': {
|
||||
id: 'id.verification.continue.upload',
|
||||
defaultMessage: 'Continue with Upload',
|
||||
description: 'Button to continue with upload.',
|
||||
},
|
||||
'id.verification.example.card.alt': {
|
||||
id: 'id.verification.example.card.alt',
|
||||
defaultMessage: 'Example of a valid identification card with a full name and photo.',
|
||||
@@ -401,26 +396,11 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Take a Photo of Yourself',
|
||||
description: 'Title for the Portrait Photo page if camera access is enabled.',
|
||||
},
|
||||
'id.verification.portrait.photo.title.upload': {
|
||||
id: 'id.verification.portrait.photo.title.upload',
|
||||
defaultMessage: 'Upload a Photo of Yourself',
|
||||
description: 'Title for the Portrait Photo page if camera access is disabled.',
|
||||
},
|
||||
'id.verification.portrait.photo.preview.alt': {
|
||||
id: 'id.verification.portrait.photo.preview.alt',
|
||||
defaultMessage: 'Preview of photo of user\'s face.',
|
||||
description: 'Alt text for the portrait photo preview.',
|
||||
},
|
||||
'id.verification.portrait.photo.instructions.camera': {
|
||||
id: 'id.verification.portrait.photo.instructions.camera',
|
||||
defaultMessage: 'When your face is in position, use the Take Photo button below to take your photo.',
|
||||
description: 'Instructions to use the camera to take a portrait photo..',
|
||||
},
|
||||
'id.verification.portrait.photo.instructions.upload': {
|
||||
id: 'id.verification.portrait.photo.instructions.upload',
|
||||
defaultMessage: 'Please upload a portrait photo. Ensure your entire face fits inside the frame and is well-lit. Supported formats: ',
|
||||
description: 'Instructions for portrait photo upload.',
|
||||
},
|
||||
'id.verification.camera.help.sight.question': {
|
||||
id: 'id.verification.camera.help.sight.question',
|
||||
defaultMessage: 'What if I can\'t see the camera image or if I can\'t see my photo to determine which side is visible?',
|
||||
@@ -451,16 +431,6 @@ const messages = defineMessages({
|
||||
defaultMessage: 'If you require assistance with taking a photo for submission, contact {siteName} support for additional suggestions.',
|
||||
description: 'Confirming what to do if the user has difficult holding their head relative to the camera.',
|
||||
},
|
||||
'id.verification.camera.help.upload.question': {
|
||||
id: 'id.verification.camera.help.upload.question',
|
||||
defaultMessage: 'What if I want to upload a photo instead?',
|
||||
description: 'Question on what to do if the user would like to upload a photo instead.',
|
||||
},
|
||||
'id.verification.camera.help.upload.answer': {
|
||||
id: 'id.verification.camera.help.upload.answer',
|
||||
defaultMessage: 'On the next page you will have the option to switch to upload mode. By selecting that option, you will be able to upload a photo instead.',
|
||||
description: 'Confirming what to do if the user would like to upload a photo.',
|
||||
},
|
||||
'id.verification.id.photo.unclear.question': {
|
||||
id: 'id.verification.id.photo.unclear.question',
|
||||
defaultMessage: 'Is your ID card image not clear or too blurry?',
|
||||
@@ -686,26 +656,6 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Switch to Camera Mode',
|
||||
description: 'Button used to switch to camera mode.',
|
||||
},
|
||||
'id.verification.choose.mode.title': {
|
||||
id: 'id.verification.choose.mode.title',
|
||||
defaultMessage: 'Photo Requirements Options',
|
||||
description: 'Title for section that allows user to choose photo mode.',
|
||||
},
|
||||
'id.verification.choose.mode.help.text': {
|
||||
id: 'id.verification.choose.mode.hep.text',
|
||||
defaultMessage: 'To complete verification, please select one of the following options to submit photos. You will be able to switch between these options throughout the process if needed.',
|
||||
description: 'Help text for section that allows user to choose photo mode.',
|
||||
},
|
||||
'id.verification.choose.mode.radio.upload': {
|
||||
id: 'id.verification.choose.mode.radio.upload',
|
||||
defaultMessage: 'Upload photos from my device',
|
||||
description: 'Radio button to choose to upload photos.',
|
||||
},
|
||||
'id.verification.choose.mode.radio.camera': {
|
||||
id: 'id.verification.choose.mode.radio.camera',
|
||||
defaultMessage: 'Take pictures using my camera',
|
||||
description: 'Radio button to choose to use camera for photos.',
|
||||
},
|
||||
});
|
||||
|
||||
export default messages;
|
||||
|
||||
@@ -45,12 +45,8 @@ export default function IdVerificationContextProvider({ children }) {
|
||||
}
|
||||
}, [authenticatedUser]);
|
||||
|
||||
const [optimizelyExperimentName, setOptimizelyExperimentName] = useState('');
|
||||
const [shouldUseCamera, setShouldUseCamera] = useState(false);
|
||||
|
||||
// The following are used to keep track of how a user has submitted photos
|
||||
const [portraitPhotoMode, setPortraitPhotoMode] = useState('');
|
||||
const [idPhotoMode, setIdPhotoMode] = useState('');
|
||||
// Default to upload for the ID image
|
||||
const [useCameraForId, setUseCameraForId] = useState(false);
|
||||
|
||||
// If the user reaches the end of the flow and goes back to retake their photos,
|
||||
// this flag ensures that they are directed straight back to the summary panel
|
||||
@@ -86,31 +82,23 @@ export default function IdVerificationContextProvider({ children }) {
|
||||
// when determining the context value nameOnAccount.
|
||||
nameOnAccount: verifiedName || authenticatedUser.name,
|
||||
profileDataManager,
|
||||
optimizelyExperimentName,
|
||||
shouldUseCamera,
|
||||
portraitPhotoMode,
|
||||
idPhotoMode,
|
||||
useCameraForId,
|
||||
reachedSummary,
|
||||
setFacePhotoFile,
|
||||
setIdPhotoFile,
|
||||
setIdPhotoName,
|
||||
setOptimizelyExperimentName,
|
||||
setShouldUseCamera,
|
||||
setPortraitPhotoMode,
|
||||
setIdPhotoMode,
|
||||
setUseCameraForId,
|
||||
setReachedSummary,
|
||||
tryGetUserMedia: async () => {
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
|
||||
setMediaAccess(MEDIA_ACCESS.GRANTED);
|
||||
setMediaStream(stream);
|
||||
setShouldUseCamera(true);
|
||||
// stop the stream, as we are not using it yet
|
||||
const tracks = stream.getTracks();
|
||||
tracks.forEach(track => track.stop());
|
||||
} catch (err) {
|
||||
setMediaAccess(MEDIA_ACCESS.DENIED);
|
||||
setShouldUseCamera(false);
|
||||
}
|
||||
},
|
||||
stopUserMedia: () => {
|
||||
|
||||
@@ -14,7 +14,6 @@ import './getUserMediaShim';
|
||||
import IdVerificationContextProvider from './IdVerificationContextProvider';
|
||||
import { VerifiedNameContextProvider } from './VerifiedNameContext';
|
||||
import ReviewRequirementsPanel from './panels/ReviewRequirementsPanel';
|
||||
import ChooseModePanel from './panels/ChooseModePanel';
|
||||
import RequestCameraAccessPanel from './panels/RequestCameraAccessPanel';
|
||||
import PortraitPhotoContextPanel from './panels/PortraitPhotoContextPanel';
|
||||
import TakePortraitPhotoPanel from './panels/TakePortraitPhotoPanel';
|
||||
@@ -57,7 +56,6 @@ function IdVerificationPage(props) {
|
||||
<IdVerificationContextProvider>
|
||||
<Switch>
|
||||
<Route path={`${path}/review-requirements`} component={ReviewRequirementsPanel} />
|
||||
<Route path={`${path}/choose-mode`} component={ChooseModePanel} />
|
||||
<Route path={`${path}/request-camera-access`} component={RequestCameraAccessPanel} />
|
||||
<Route path={`${path}/portrait-photo-context`} component={PortraitPhotoContextPanel} />
|
||||
<Route path={`${path}/take-portrait-photo`} component={TakePortraitPhotoPanel} />
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Alert } from '@edx/paragon';
|
||||
import messages from './IdVerification.messages';
|
||||
import SupportedMediaTypes from './SupportedMediaTypes';
|
||||
|
||||
export default function ImageFileUpload({ onFileChange, setPhotoMode, intl }) {
|
||||
export default function ImageFileUpload({ onFileChange, intl }) {
|
||||
const [error, setError] = useState(null);
|
||||
const errorTypes = {
|
||||
invalidFileType: 'invalidFileType',
|
||||
@@ -28,7 +28,6 @@ export default function ImageFileUpload({ onFileChange, setPhotoMode, intl }) {
|
||||
const fileReader = new FileReader();
|
||||
fileReader.addEventListener('load', () => {
|
||||
onFileChange(fileReader.result);
|
||||
setPhotoMode('upload');
|
||||
});
|
||||
fileReader.readAsDataURL(fileObject);
|
||||
}
|
||||
@@ -59,6 +58,5 @@ export default function ImageFileUpload({ onFileChange, setPhotoMode, intl }) {
|
||||
|
||||
ImageFileUpload.propTypes = {
|
||||
onFileChange: PropTypes.func.isRequired,
|
||||
setPhotoMode: PropTypes.func.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
@@ -64,9 +64,6 @@ export async function submitIdVerification(verificationData) {
|
||||
facePhotoFile: 'face_image',
|
||||
idPhotoFile: 'photo_id_image',
|
||||
idPhotoName: 'full_name',
|
||||
optimizelyExperimentName: 'experiment_name',
|
||||
portraitPhotoMode: 'portrait_photo_mode',
|
||||
idPhotoMode: 'id_photo_mode',
|
||||
};
|
||||
const postData = {};
|
||||
// Don't include blank/null/undefined values.
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Form } from '@edx/paragon';
|
||||
|
||||
import { useNextPanelSlug } from '../routing-utilities';
|
||||
import BasePanel from './BasePanel';
|
||||
import IdVerificationContext from '../IdVerificationContext';
|
||||
import messages from '../IdVerification.messages';
|
||||
|
||||
function ChooseModePanel(props) {
|
||||
const panelSlug = 'choose-mode';
|
||||
const { userId, shouldUseCamera, setShouldUseCamera } = useContext(IdVerificationContext);
|
||||
|
||||
function onPhotoModeChange(value) {
|
||||
setShouldUseCamera(value);
|
||||
const mode = value ? 'camera' : 'upload';
|
||||
const eventName = `edx.id_verification.choose.${mode}`;
|
||||
sendTrackEvent(eventName, {
|
||||
category: 'id_verification',
|
||||
user_id: userId,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<BasePanel
|
||||
name={panelSlug}
|
||||
title={props.intl.formatMessage(messages['id.verification.choose.mode.title'])}
|
||||
>
|
||||
<p>
|
||||
{props.intl.formatMessage(messages['id.verification.choose.mode.help.text'])}
|
||||
</p>
|
||||
<fieldset>
|
||||
<Form.Group controlId="formChoosePhotoOption" style={{ marginLeft: '1.25rem' }}>
|
||||
<Form.Check
|
||||
type="radio"
|
||||
id="useUploadMode"
|
||||
label={props.intl.formatMessage(messages['id.verification.choose.mode.radio.upload'])}
|
||||
name="photoMode"
|
||||
checked={!shouldUseCamera}
|
||||
onChange={() => onPhotoModeChange(false)}
|
||||
/>
|
||||
<Form.Check
|
||||
type="radio"
|
||||
id="useCameraMode"
|
||||
label={props.intl.formatMessage(messages['id.verification.choose.mode.radio.camera'])}
|
||||
name="photoMode"
|
||||
checked={shouldUseCamera}
|
||||
onChange={() => onPhotoModeChange(true)}
|
||||
/>
|
||||
</Form.Group>
|
||||
</fieldset>
|
||||
<div className="action-row">
|
||||
<Link to={useNextPanelSlug(panelSlug)} className="btn btn-primary" data-testid="next-button">
|
||||
{props.intl.formatMessage(messages['id.verification.next'])}
|
||||
</Link>
|
||||
</div>
|
||||
</BasePanel>
|
||||
);
|
||||
}
|
||||
|
||||
ChooseModePanel.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(ChooseModePanel);
|
||||
@@ -19,7 +19,7 @@ function RequestCameraAccessPanel(props) {
|
||||
const panelSlug = 'request-camera-access';
|
||||
const nextPanelSlug = useNextPanelSlug(panelSlug);
|
||||
const {
|
||||
tryGetUserMedia, mediaAccess, userId, optimizelyExperimentName,
|
||||
tryGetUserMedia, mediaAccess, userId,
|
||||
} = useContext(IdVerificationContext);
|
||||
const browserName = Bowser.parse(window.navigator.userAgent).browser.name;
|
||||
|
||||
@@ -54,12 +54,6 @@ function RequestCameraAccessPanel(props) {
|
||||
</a>
|
||||
);
|
||||
|
||||
const nextButtonLink = (
|
||||
<Link to={nextPanelSlug} className="btn btn-primary" data-testid="next-button">
|
||||
{props.intl.formatMessage(messages['id.verification.continue.upload'])}
|
||||
</Link>
|
||||
);
|
||||
|
||||
return (
|
||||
<BasePanel
|
||||
name={panelSlug}
|
||||
@@ -105,7 +99,7 @@ function RequestCameraAccessPanel(props) {
|
||||
</p>
|
||||
<EnableCameraDirectionsPanel browserName={browserName} intl={props.intl} />
|
||||
<div className="action-row">
|
||||
{optimizelyExperimentName ? nextButtonLink : returnLink}
|
||||
{returnLink}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -117,7 +111,7 @@ function RequestCameraAccessPanel(props) {
|
||||
</p>
|
||||
<UnsupportedCameraDirectionsPanel browserName={browserName} intl={props.intl} />
|
||||
<div className="action-row">
|
||||
{optimizelyExperimentName ? nextButtonLink : returnLink}
|
||||
{returnLink}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -13,31 +13,15 @@ import messages from '../IdVerification.messages';
|
||||
import exampleCard from '../assets/example-card.png';
|
||||
|
||||
function ReviewRequirementsPanel(props) {
|
||||
const {
|
||||
userId, profileDataManager, setOptimizelyExperimentName,
|
||||
} = useContext(IdVerificationContext);
|
||||
const { userId, profileDataManager } = useContext(IdVerificationContext);
|
||||
const panelSlug = 'review-requirements';
|
||||
const nextPanelSlug = useNextPanelSlug(panelSlug);
|
||||
|
||||
const getExperiments = () => {
|
||||
const {
|
||||
experimentVariables: {
|
||||
experimentName = '',
|
||||
} = {},
|
||||
} = window;
|
||||
|
||||
if (experimentName) {
|
||||
setOptimizelyExperimentName(experimentName);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
sendTrackEvent('edx.id_verification.started', {
|
||||
category: 'id_verification',
|
||||
user_id: userId,
|
||||
});
|
||||
|
||||
getExperiments();
|
||||
}, [userId]);
|
||||
|
||||
function renderManagedProfileMessage() {
|
||||
|
||||
@@ -26,10 +26,7 @@ function SummaryPanel(props) {
|
||||
nameOnAccount,
|
||||
idPhotoName,
|
||||
stopUserMedia,
|
||||
optimizelyExperimentName,
|
||||
setReachedSummary,
|
||||
portraitPhotoMode,
|
||||
idPhotoMode,
|
||||
} = useContext(IdVerificationContext);
|
||||
const nameToBeUsed = idPhotoName || nameOnAccount || '';
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
@@ -79,11 +76,6 @@ function SummaryPanel(props) {
|
||||
*/
|
||||
verificationData.idPhotoName = nameOnAccount;
|
||||
}
|
||||
if (optimizelyExperimentName) {
|
||||
verificationData.optimizelyExperimentName = optimizelyExperimentName;
|
||||
verificationData.portraitPhotoMode = portraitPhotoMode;
|
||||
verificationData.idPhotoMode = idPhotoMode;
|
||||
}
|
||||
const result = await submitIdVerification(verificationData);
|
||||
if (result.success) {
|
||||
stopUserMedia();
|
||||
@@ -208,7 +200,7 @@ function SummaryPanel(props) {
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
{!optimizelyExperimentName && <CameraHelpWithUpload />}
|
||||
<CameraHelpWithUpload />
|
||||
<div className="form-group">
|
||||
<label htmlFor="name-to-be-used" className="font-weight-bold">
|
||||
{props.intl.formatMessage(messages['id.verification.name.label'])}
|
||||
|
||||
@@ -17,9 +17,7 @@ import SupportedMediaTypes from '../SupportedMediaTypes';
|
||||
function TakeIdPhotoPanel(props) {
|
||||
const panelSlug = 'take-id-photo';
|
||||
const nextPanelSlug = useNextPanelSlug(panelSlug);
|
||||
const {
|
||||
setIdPhotoFile, idPhotoFile, optimizelyExperimentName, shouldUseCamera, setIdPhotoMode,
|
||||
} = useContext(IdVerificationContext);
|
||||
const { setIdPhotoFile, idPhotoFile, useCameraForId } = useContext(IdVerificationContext);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -31,17 +29,24 @@ function TakeIdPhotoPanel(props) {
|
||||
<BasePanel
|
||||
name={panelSlug}
|
||||
focusOnMount={!mounted}
|
||||
title={shouldUseCamera ? props.intl.formatMessage(messages['id.verification.id.photo.title.camera']) : props.intl.formatMessage(messages['id.verification.id.photo.title.upload'])}
|
||||
title={useCameraForId
|
||||
? props.intl.formatMessage(messages['id.verification.id.photo.title.camera'])
|
||||
: props.intl.formatMessage(messages['id.verification.id.photo.title.upload'])}
|
||||
>
|
||||
<div>
|
||||
{idPhotoFile && !shouldUseCamera && <ImagePreview src={idPhotoFile} alt={props.intl.formatMessage(messages['id.verification.id.photo.preview.alt'])} />}
|
||||
{idPhotoFile && !useCameraForId && (
|
||||
<ImagePreview
|
||||
src={idPhotoFile}
|
||||
alt={props.intl.formatMessage(messages['id.verification.id.photo.preview.alt'])}
|
||||
/>
|
||||
)}
|
||||
|
||||
{shouldUseCamera ? (
|
||||
{useCameraForId ? (
|
||||
<div>
|
||||
<p>
|
||||
{props.intl.formatMessage(messages['id.verification.id.photo.instructions.camera'])}
|
||||
</p>
|
||||
<Camera onImageCapture={setIdPhotoFile} setPhotoMode={setIdPhotoMode} isPortrait={false} />
|
||||
<Camera onImageCapture={setIdPhotoFile} isPortrait={false} />
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ marginBottom: '1.25rem' }}>
|
||||
@@ -49,12 +54,12 @@ function TakeIdPhotoPanel(props) {
|
||||
{props.intl.formatMessage(messages['id.verification.id.photo.instructions.upload'])}
|
||||
<SupportedMediaTypes />
|
||||
</p>
|
||||
<ImageFileUpload onFileChange={setIdPhotoFile} setPhotoMode={setIdPhotoMode} intl={props.intl} />
|
||||
<ImageFileUpload onFileChange={setIdPhotoFile} intl={props.intl} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{shouldUseCamera && !optimizelyExperimentName && <CameraHelp />}
|
||||
<CollapsibleImageHelp isPortrait={false} />
|
||||
{useCameraForId && <CameraHelp />}
|
||||
<CollapsibleImageHelp />
|
||||
<div className="action-row" style={{ visibility: idPhotoFile ? 'unset' : 'hidden' }}>
|
||||
<Link to={nextPanelSlug} className="btn btn-primary" data-testid="next-button">
|
||||
{props.intl.formatMessage(messages['id.verification.next'])}
|
||||
|
||||
@@ -4,22 +4,16 @@ import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { useNextPanelSlug } from '../routing-utilities';
|
||||
import BasePanel from './BasePanel';
|
||||
import ImageFileUpload from '../ImageFileUpload';
|
||||
import ImagePreview from '../ImagePreview';
|
||||
import Camera from '../Camera';
|
||||
import CameraHelp from '../CameraHelp';
|
||||
import IdVerificationContext from '../IdVerificationContext';
|
||||
|
||||
import messages from '../IdVerification.messages';
|
||||
import CollapsibleImageHelp from '../CollapsibleImageHelp';
|
||||
import SupportedMediaTypes from '../SupportedMediaTypes';
|
||||
|
||||
function TakePortraitPhotoPanel(props) {
|
||||
const panelSlug = 'take-portrait-photo';
|
||||
const nextPanelSlug = useNextPanelSlug(panelSlug);
|
||||
const {
|
||||
setFacePhotoFile, facePhotoFile, shouldUseCamera, optimizelyExperimentName, setPortraitPhotoMode,
|
||||
} = useContext(IdVerificationContext);
|
||||
const { setFacePhotoFile, facePhotoFile } = useContext(IdVerificationContext);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -31,30 +25,15 @@ function TakePortraitPhotoPanel(props) {
|
||||
<BasePanel
|
||||
name={panelSlug}
|
||||
focusOnMount={!mounted}
|
||||
title={shouldUseCamera ? props.intl.formatMessage(messages['id.verification.portrait.photo.title.camera']) : props.intl.formatMessage(messages['id.verification.portrait.photo.title.upload'])}
|
||||
title={props.intl.formatMessage(messages['id.verification.portrait.photo.title.camera'])}
|
||||
>
|
||||
<div>
|
||||
{facePhotoFile && !shouldUseCamera && <ImagePreview src={facePhotoFile} alt={props.intl.formatMessage(messages['id.verification.portrait.photo.preview.alt'])} />}
|
||||
|
||||
{shouldUseCamera ? (
|
||||
<div>
|
||||
<p>
|
||||
{props.intl.formatMessage(messages['id.verification.portrait.photo.instructions.camera'])}
|
||||
</p>
|
||||
<Camera onImageCapture={setFacePhotoFile} setPhotoMode={setPortraitPhotoMode} isPortrait />
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ marginBottom: '1.25rem' }}>
|
||||
<p data-testid="upload-text">
|
||||
{props.intl.formatMessage(messages['id.verification.portrait.photo.instructions.upload'])}
|
||||
<SupportedMediaTypes />
|
||||
</p>
|
||||
<ImageFileUpload onFileChange={setFacePhotoFile} setPhotoMode={setPortraitPhotoMode} intl={props.intl} />
|
||||
</div>
|
||||
)}
|
||||
<p>
|
||||
{props.intl.formatMessage(messages['id.verification.portrait.photo.instructions.camera'])}
|
||||
</p>
|
||||
<Camera onImageCapture={setFacePhotoFile} isPortrait />
|
||||
</div>
|
||||
{shouldUseCamera && !optimizelyExperimentName && <CameraHelp isPortrait />}
|
||||
<CollapsibleImageHelp isPortrait />
|
||||
<CameraHelp isPortrait />
|
||||
<div className="action-row" style={{ visibility: facePhotoFile ? 'unset' : 'hidden' }}>
|
||||
<Link to={nextPanelSlug} className="btn btn-primary" data-testid="next-button">
|
||||
{props.intl.formatMessage(messages['id.verification.next'])}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useContext } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import IdVerificationContext, { MEDIA_ACCESS } from './IdVerificationContext';
|
||||
import IdVerificationContext from './IdVerificationContext';
|
||||
|
||||
const SLUGS = {
|
||||
REVIEW_REQUIREMENTS: 'review-requirements',
|
||||
@@ -17,7 +16,6 @@ const SLUGS = {
|
||||
|
||||
const panelSteps = [
|
||||
SLUGS.REVIEW_REQUIREMENTS,
|
||||
SLUGS.CHOOSE_MODE,
|
||||
SLUGS.REQUEST_CAMERA_ACCESS,
|
||||
SLUGS.PORTRAIT_PHOTO_CONTEXT,
|
||||
SLUGS.TAKE_PORTRAIT_PHOTO,
|
||||
@@ -31,15 +29,7 @@ const panelSteps = [
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const useNextPanelSlug = (originSlug) => {
|
||||
// Go back to the summary view if that's where they came from
|
||||
const location = useLocation();
|
||||
const isFromPortrait = location.state && location.state.fromPortraitCapture;
|
||||
const isFromId = location.state && location.state.fromIdCapture;
|
||||
const {
|
||||
mediaAccess,
|
||||
optimizelyExperimentName,
|
||||
reachedSummary,
|
||||
shouldUseCamera,
|
||||
} = useContext(IdVerificationContext);
|
||||
const { reachedSummary } = useContext(IdVerificationContext);
|
||||
|
||||
const canRerouteToSummary = [
|
||||
SLUGS.TAKE_PORTRAIT_PHOTO,
|
||||
@@ -51,32 +41,6 @@ export const useNextPanelSlug = (originSlug) => {
|
||||
return SLUGS.SUMMARY;
|
||||
}
|
||||
|
||||
// the following are used as part of an A/B experiment
|
||||
if (isFromPortrait) {
|
||||
if (mediaAccess === MEDIA_ACCESS.GRANTED) {
|
||||
return SLUGS.PORTRAIT_PHOTO_CONTEXT;
|
||||
}
|
||||
return SLUGS.TAKE_PORTRAIT_PHOTO;
|
||||
}
|
||||
if (isFromId) {
|
||||
if (mediaAccess === MEDIA_ACCESS.GRANTED) {
|
||||
return SLUGS.ID_CONTEXT;
|
||||
}
|
||||
return SLUGS.TAKE_ID_PHOTO;
|
||||
}
|
||||
if (originSlug === SLUGS.REVIEW_REQUIREMENTS && !optimizelyExperimentName) {
|
||||
return SLUGS.REQUEST_CAMERA_ACCESS;
|
||||
}
|
||||
if (originSlug === SLUGS.CHOOSE_MODE && !shouldUseCamera) {
|
||||
return SLUGS.TAKE_PORTRAIT_PHOTO;
|
||||
}
|
||||
if (originSlug === SLUGS.TAKE_PORTRAIT_PHOTO && !shouldUseCamera) {
|
||||
return SLUGS.TAKE_ID_PHOTO;
|
||||
}
|
||||
if (originSlug === SLUGS.REQUEST_CAMERA_ACCESS && mediaAccess !== MEDIA_ACCESS.GRANTED) {
|
||||
return SLUGS.TAKE_PORTRAIT_PHOTO;
|
||||
}
|
||||
|
||||
const nextIndex = panelSteps.indexOf(originSlug) + 1;
|
||||
return nextIndex < panelSteps.length ? panelSteps[nextIndex] : null;
|
||||
};
|
||||
@@ -84,11 +48,8 @@ export const useNextPanelSlug = (originSlug) => {
|
||||
// check if the user is too far into the flow and if so, return the slug of the
|
||||
// furthest panel they are allow to be.
|
||||
export const useVerificationRedirectSlug = (slug) => {
|
||||
const { facePhotoFile, idPhotoFile, optimizelyExperimentName } = useContext(IdVerificationContext);
|
||||
const { facePhotoFile, idPhotoFile } = useContext(IdVerificationContext);
|
||||
const indexOfCurrentPanel = panelSteps.indexOf(slug);
|
||||
if (!optimizelyExperimentName && slug === SLUGS.CHOOSE_MODE) {
|
||||
return SLUGS.REVIEW_REQUIREMENTS;
|
||||
}
|
||||
if (!facePhotoFile) {
|
||||
if (indexOfCurrentPanel > panelSteps.indexOf(SLUGS.TAKE_PORTRAIT_PHOTO)) {
|
||||
return SLUGS.PORTRAIT_PHOTO_CONTEXT;
|
||||
|
||||
@@ -27,7 +27,6 @@ describe('SubmittedPanel', () => {
|
||||
const defaultProps = {
|
||||
intl: {},
|
||||
onImageCapture: jest.fn(),
|
||||
setPhotoMode: jest.fn(),
|
||||
isPortrait: true,
|
||||
};
|
||||
|
||||
@@ -57,7 +56,6 @@ describe('SubmittedPanel', () => {
|
||||
expect(button).toHaveTextContent('Take Photo');
|
||||
fireEvent.click(button);
|
||||
expect(defaultProps.onImageCapture).toHaveBeenCalled();
|
||||
expect(defaultProps.setPhotoMode).toHaveBeenCalledWith('camera');
|
||||
});
|
||||
|
||||
it('shows correct help text for portrait photo capture', async () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { Router } from 'react-router-dom';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import {
|
||||
render, cleanup, screen, act, fireEvent,
|
||||
render, cleanup, screen, act,
|
||||
} from '@testing-library/react';
|
||||
import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import * as analytics from '@edx/frontend-platform/analytics';
|
||||
@@ -22,71 +22,18 @@ const IntlCollapsible = injectIntl(CollapsibleImageHelp);
|
||||
const history = createMemoryHistory();
|
||||
|
||||
describe('CollapsibleImageHelpPanel', () => {
|
||||
const defaultProps = {
|
||||
intl: {},
|
||||
isPortrait: true,
|
||||
};
|
||||
const defaultProps = { intl: {} };
|
||||
|
||||
const contextValue = {
|
||||
shouldUseCamera: true,
|
||||
setShouldUseCamera: jest.fn(),
|
||||
optimizelyExperimentName: '',
|
||||
mediaAccess: 'granted',
|
||||
useCameraForId: true,
|
||||
setUseCameraForId: jest.fn(),
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('does not return if not part of experiment', async () => {
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlCollapsible {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
|
||||
const titleText = screen.queryByText('Upload a Photo Instead');
|
||||
expect(titleText).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not return if media access denied or unsupported', async () => {
|
||||
let titleText = '';
|
||||
contextValue.mediaAccess = 'denied';
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlCollapsible {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
|
||||
titleText = screen.queryByText('Upload a Photo Instead');
|
||||
expect(titleText).not.toBeInTheDocument();
|
||||
|
||||
contextValue.mediaAccess = 'unsupported';
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlCollapsible {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
|
||||
titleText = screen.queryByText('Upload a Photo Instead');
|
||||
expect(titleText).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows the correct text if user should switch to upload', async () => {
|
||||
contextValue.optimizelyExperimentName = 'test';
|
||||
contextValue.mediaAccess = 'granted';
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
@@ -106,9 +53,7 @@ describe('CollapsibleImageHelpPanel', () => {
|
||||
});
|
||||
|
||||
it('shows the correct text if user should switch to camera', async () => {
|
||||
contextValue.optimizelyExperimentName = 'test';
|
||||
contextValue.mediaAccess = 'granted';
|
||||
contextValue.shouldUseCamera = false;
|
||||
contextValue.useCameraForId = false;
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
@@ -126,27 +71,4 @@ describe('CollapsibleImageHelpPanel', () => {
|
||||
const button = screen.getByTestId('toggle-button');
|
||||
expect(button).toHaveTextContent('Switch to Camera Mode');
|
||||
});
|
||||
|
||||
it('shows the correct text if user should switch to camera with pending media access', async () => {
|
||||
contextValue.optimizelyExperimentName = 'test';
|
||||
contextValue.mediaAccess = 'pending';
|
||||
contextValue.shouldUseCamera = false;
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlCollapsible {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
|
||||
const titleText = screen.getByText('Use Your Camera Instead');
|
||||
expect(titleText).toBeInTheDocument();
|
||||
const helpText = screen.getByTestId('help-text');
|
||||
expect(helpText.textContent).toContain('If you are having trouble uploading a photo above');
|
||||
const accessLink = screen.getByTestId('access-link');
|
||||
fireEvent.click(accessLink);
|
||||
expect(history.location.pathname).toEqual('/request-camera-access');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Router } from 'react-router-dom';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import {
|
||||
render, cleanup, act, screen, fireEvent,
|
||||
} from '@testing-library/react';
|
||||
import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import IdVerificationContext from '../../IdVerificationContext';
|
||||
import ChooseModePanel from '../../panels/ChooseModePanel';
|
||||
|
||||
jest.mock('@edx/frontend-platform/analytics', () => ({
|
||||
sendTrackEvent: jest.fn(),
|
||||
}));
|
||||
|
||||
const IntlChooseModePanel = injectIntl(ChooseModePanel);
|
||||
|
||||
const history = createMemoryHistory();
|
||||
|
||||
describe('ChooseModePanel', () => {
|
||||
const defaultProps = {
|
||||
intl: {},
|
||||
};
|
||||
|
||||
const contextValue = {
|
||||
optimizelyExperimentName: 'test',
|
||||
shouldUseCamera: false,
|
||||
reachedSummary: false,
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('renders correctly', async () => {
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlChooseModePanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
|
||||
// check that radio button for upload is selected
|
||||
const uploadRadioButton = await screen.findByLabelText('Upload photos from my device');
|
||||
expect(uploadRadioButton).toBeChecked();
|
||||
|
||||
// check that if upload is selected, next button goes to correct screen
|
||||
const nextButton = await screen.findByTestId('next-button');
|
||||
expect(nextButton.getAttribute('href')).toEqual('/take-portrait-photo');
|
||||
});
|
||||
|
||||
it('renders correctly if user wants to use camera', async () => {
|
||||
contextValue.shouldUseCamera = true;
|
||||
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlChooseModePanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
|
||||
// check that radio button for camera is selected
|
||||
const cameraRadioButton = await screen.findByLabelText('Take pictures using my camera');
|
||||
expect(cameraRadioButton).toBeChecked();
|
||||
|
||||
// check that if upload is selected, next button goes to correct screen
|
||||
const nextButton = await screen.findByTestId('next-button');
|
||||
expect(nextButton.getAttribute('href')).toEqual('/request-camera-access');
|
||||
});
|
||||
|
||||
it('reroutes correctly if reachedSummary is true', async () => {
|
||||
contextValue.shouldUseCamera = true;
|
||||
contextValue.reachedSummary = true;
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlChooseModePanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const nextButton = await screen.findByTestId('next-button');
|
||||
fireEvent.click(nextButton);
|
||||
expect(history.location.pathname).toEqual('/request-camera-access');
|
||||
});
|
||||
|
||||
it('redirects if user is not part of experiment', async () => {
|
||||
contextValue.optimizelyExperimentName = '';
|
||||
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlChooseModePanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
|
||||
// check that radio button is not in document
|
||||
const cameraRadioButton = await screen.queryByLabelText('Take pictures using my camera');
|
||||
expect(cameraRadioButton).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -23,7 +23,6 @@ describe('IdContextPanel', () => {
|
||||
};
|
||||
|
||||
const contextValue = {
|
||||
optimizelyExperimentName: '',
|
||||
facePhotoFile: 'test.jpg',
|
||||
reachedSummary: false,
|
||||
};
|
||||
@@ -62,33 +61,4 @@ describe('IdContextPanel', () => {
|
||||
fireEvent.click(button);
|
||||
expect(history.location.pathname).toEqual('/take-id-photo');
|
||||
});
|
||||
|
||||
it('does not show help text for photo upload if not part of experiment', async () => {
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlIdContextPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const title = await screen.queryByText('What if I want to upload a photo instead?');
|
||||
expect(title).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows help text for photo upload if part of experiment', async () => {
|
||||
contextValue.optimizelyExperimentName = 'test';
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlIdContextPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const title = await screen.queryByText('What if I want to upload a photo instead?');
|
||||
expect(title).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,10 +22,7 @@ describe('PortraitPhotoContextPanel', () => {
|
||||
intl: {},
|
||||
};
|
||||
|
||||
const contextValue = {
|
||||
optimizelyExperimentName: '',
|
||||
reachedSummary: false,
|
||||
};
|
||||
const contextValue = { reachedSummary: false };
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
@@ -61,33 +58,4 @@ describe('PortraitPhotoContextPanel', () => {
|
||||
fireEvent.click(button);
|
||||
expect(history.location.pathname).toEqual('/take-portrait-photo');
|
||||
});
|
||||
|
||||
it('does not show help text for photo upload if not part of experiment', async () => {
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlPortraitPhotoContextPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const title = await screen.queryByText('What if I want to upload a photo instead?');
|
||||
expect(title).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows help text for photo upload if part of experiment', async () => {
|
||||
contextValue.optimizelyExperimentName = 'test';
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlPortraitPhotoContextPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const title = await screen.queryByText('What if I want to upload a photo instead?');
|
||||
expect(title).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@ import { createMemoryHistory } from 'history';
|
||||
import {
|
||||
render, screen, cleanup, act, fireEvent,
|
||||
} from '@testing-library/react';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import IdVerificationContext from '../../IdVerificationContext';
|
||||
import RequestCameraAccessPanel from '../../panels/RequestCameraAccessPanel';
|
||||
@@ -84,27 +85,6 @@ describe('RequestCameraAccessPanel', () => {
|
||||
expect(text).toHaveTextContent(/It looks like we're unable to access your camera./);
|
||||
});
|
||||
|
||||
it('renders correctly with media access denied in optimizely experiment', async () => {
|
||||
contextValue.mediaAccess = 'denied';
|
||||
contextValue.optimizelyExperimentName = 'test';
|
||||
Bowser.parse = jest.fn().mockReturnValue({ browser: { name: '' } });
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlRequestCameraAccessPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const text = await screen.findByTestId('camera-access-failure');
|
||||
expect(text).toHaveTextContent(/It looks like we're unable to access your camera./);
|
||||
const nextButton = await screen.findByText('Continue with Upload');
|
||||
fireEvent.click(nextButton);
|
||||
expect(history.location.pathname).toEqual('/take-portrait-photo');
|
||||
contextValue.optimizelyExperimentName = '';
|
||||
});
|
||||
|
||||
it('renders correctly with media access unsupported with Chrome browser', async () => {
|
||||
contextValue.mediaAccess = 'unsupported';
|
||||
Bowser.parse = jest.fn().mockReturnValue({ browser: { name: 'Chrome' } });
|
||||
@@ -203,9 +183,25 @@ describe('RequestCameraAccessPanel', () => {
|
||||
expect(text).toHaveTextContent(/Open the Flash Player/);
|
||||
});
|
||||
|
||||
it('reroutes correctly to portrait context', async () => {
|
||||
it('routes to dashboard when camera access is denied', async () => {
|
||||
contextValue.mediaAccess = 'denied';
|
||||
|
||||
Bowser.parse = jest.fn().mockReturnValue({ browser: { name: '' } });
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlRequestCameraAccessPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const button = await screen.findByRole('link');
|
||||
expect(button).toHaveAttribute('href', `${getConfig().LMS_BASE_URL}/dashboard`);
|
||||
});
|
||||
|
||||
it('routes correctly to portrait context', async () => {
|
||||
contextValue.mediaAccess = 'granted';
|
||||
history.location.state = { fromPortraitCapture: true };
|
||||
|
||||
Bowser.parse = jest.fn().mockReturnValue({ browser: { name: '' } });
|
||||
await act(async () => render((
|
||||
@@ -221,105 +217,4 @@ describe('RequestCameraAccessPanel', () => {
|
||||
fireEvent.click(button);
|
||||
expect(history.location.pathname).toEqual('/portrait-photo-context');
|
||||
});
|
||||
|
||||
it('reroutes correctly to ID context', async () => {
|
||||
contextValue.mediaAccess = 'granted';
|
||||
history.location.state = { fromIdCapture: true };
|
||||
|
||||
Bowser.parse = jest.fn().mockReturnValue({ browser: { name: '' } });
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlRequestCameraAccessPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const button = await screen.findByTestId('next-button');
|
||||
fireEvent.click(button);
|
||||
expect(history.location.pathname).toEqual('/id-context');
|
||||
});
|
||||
|
||||
it('reroutes to portrait context when reachedSummary is true', async () => {
|
||||
contextValue.mediaAccess = 'granted';
|
||||
contextValue.reachedSummary = true;
|
||||
history.location.state = { fromPortraitCapture: true };
|
||||
|
||||
Bowser.parse = jest.fn().mockReturnValue({ browser: { name: '' } });
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlRequestCameraAccessPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const button = await screen.findByTestId('next-button');
|
||||
fireEvent.click(button);
|
||||
expect(history.location.pathname).toEqual('/portrait-photo-context');
|
||||
});
|
||||
|
||||
it('reroutes to ID context when reachedSummary is true', async () => {
|
||||
contextValue.mediaAccess = 'granted';
|
||||
contextValue.reachedSummary = true;
|
||||
history.location.state = { fromIdCapture: true };
|
||||
|
||||
Bowser.parse = jest.fn().mockReturnValue({ browser: { name: '' } });
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlRequestCameraAccessPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const button = await screen.findByTestId('next-button');
|
||||
fireEvent.click(button);
|
||||
expect(history.location.pathname).toEqual('/id-context');
|
||||
});
|
||||
|
||||
it('reroutes correctly to portrait context with no media access', async () => {
|
||||
contextValue.mediaAccess = 'denied';
|
||||
contextValue.optimizelyExperimentName = 'test';
|
||||
history.location.state = { fromPortraitCapture: true };
|
||||
|
||||
Bowser.parse = jest.fn().mockReturnValue({ browser: { name: '' } });
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlRequestCameraAccessPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const button = await screen.findByTestId('next-button');
|
||||
fireEvent.click(button);
|
||||
expect(history.location.pathname).toEqual('/take-portrait-photo');
|
||||
contextValue.optimizelyExperimentName = '';
|
||||
});
|
||||
|
||||
it('reroutes correctly to ID context with no media access', async () => {
|
||||
contextValue.mediaAccess = 'denied';
|
||||
contextValue.optimizelyExperimentName = 'test';
|
||||
history.location.state = { fromIdCapture: true };
|
||||
|
||||
Bowser.parse = jest.fn().mockReturnValue({ browser: { name: '' } });
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlRequestCameraAccessPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const button = await screen.findByTestId('next-button');
|
||||
fireEvent.click(button);
|
||||
expect(history.location.pathname).toEqual('/take-id-photo');
|
||||
contextValue.optimizelyExperimentName = '';
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@ describe('ReviewRequirementsPanel', () => {
|
||||
intl: {},
|
||||
};
|
||||
|
||||
const context = { setOptimizelyExperimentName: jest.fn() };
|
||||
const context = {};
|
||||
|
||||
const getPanel = async () => {
|
||||
await act(async () => render((
|
||||
@@ -47,13 +47,6 @@ describe('ReviewRequirementsPanel', () => {
|
||||
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();
|
||||
|
||||
@@ -32,9 +32,6 @@ describe('SummaryPanel', () => {
|
||||
idPhotoFile: 'test.jpg',
|
||||
nameOnAccount: 'test name',
|
||||
idPhotoName: 'test name',
|
||||
optimizelyExperimentName: 'test-experiment',
|
||||
portraitPhotoMode: 'camera',
|
||||
idPhotoMode: 'upload',
|
||||
stopUserMedia: jest.fn(),
|
||||
setReachedSummary: jest.fn(),
|
||||
};
|
||||
@@ -76,13 +73,11 @@ describe('SummaryPanel', () => {
|
||||
});
|
||||
|
||||
it('allows user to upload ID photo', async () => {
|
||||
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();
|
||||
appContextValue.optimizelyExperimentName = 'test-experiment';
|
||||
});
|
||||
|
||||
it('displays warning if account is managed by a third party', async () => {
|
||||
@@ -97,9 +92,6 @@ describe('SummaryPanel', () => {
|
||||
facePhotoFile: appContextValue.facePhotoFile,
|
||||
idPhotoFile: appContextValue.idPhotoFile,
|
||||
idPhotoName: appContextValue.idPhotoName,
|
||||
optimizelyExperimentName: appContextValue.optimizelyExperimentName,
|
||||
portraitPhotoMode: appContextValue.portraitPhotoMode,
|
||||
idPhotoMode: appContextValue.idPhotoMode,
|
||||
courseRunKey: null,
|
||||
};
|
||||
await getPanel();
|
||||
@@ -114,9 +106,6 @@ describe('SummaryPanel', () => {
|
||||
const verificationData = {
|
||||
facePhotoFile: appContextValue.facePhotoFile,
|
||||
idPhotoFile: appContextValue.idPhotoFile,
|
||||
portraitPhotoMode: appContextValue.portraitPhotoMode,
|
||||
idPhotoMode: appContextValue.idPhotoMode,
|
||||
optimizelyExperimentName: appContextValue.optimizelyExperimentName,
|
||||
courseRunKey: null,
|
||||
idPhotoName: appContextValue.nameOnAccount,
|
||||
};
|
||||
@@ -131,9 +120,6 @@ describe('SummaryPanel', () => {
|
||||
const verificationData = {
|
||||
facePhotoFile: appContextValue.facePhotoFile,
|
||||
idPhotoFile: appContextValue.idPhotoFile,
|
||||
portraitPhotoMode: appContextValue.portraitPhotoMode,
|
||||
idPhotoMode: appContextValue.idPhotoMode,
|
||||
optimizelyExperimentName: appContextValue.optimizelyExperimentName,
|
||||
courseRunKey: null,
|
||||
idPhotoName: appContextValue.nameOnAccount,
|
||||
};
|
||||
@@ -208,11 +194,4 @@ describe('SummaryPanel', () => {
|
||||
'One or more of the files you have uploaded is in an unsupported format. Please choose from the following:',
|
||||
);
|
||||
});
|
||||
|
||||
it('does not show ID upload option if user is in experiment', async () => {
|
||||
await getPanel();
|
||||
const collapsible = await screen.queryByTestId('collapsible');
|
||||
expect(collapsible).not.toBeInTheDocument();
|
||||
appContextValue.optimizelyExperimentName = 'test-experiment';
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,6 +28,7 @@ describe('TakeIdPhotoPanel', () => {
|
||||
idPhotoFile: null,
|
||||
reachedSummary: false,
|
||||
setIdPhotoFile: jest.fn(),
|
||||
useCameraForId: false,
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
@@ -83,9 +84,6 @@ describe('TakeIdPhotoPanel', () => {
|
||||
});
|
||||
|
||||
it('shows correct text if user should use upload', async () => {
|
||||
contextValue.optimizelyExperimentName = 'test';
|
||||
contextValue.shouldUseCamera = false;
|
||||
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
|
||||
@@ -28,7 +28,6 @@ describe('TakePortraitPhotoPanel', () => {
|
||||
idPhotoFile: null,
|
||||
reachedSummary: false,
|
||||
setFacePhotoFile: jest.fn(),
|
||||
setShouldUseCamera: jest.fn(),
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
@@ -51,7 +50,6 @@ describe('TakePortraitPhotoPanel', () => {
|
||||
|
||||
it('shows next button after photo is taken and routes to IdContextPanel', async () => {
|
||||
contextValue.facePhotoFile = 'test.jpg';
|
||||
contextValue.shouldUseCamera = true;
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
@@ -84,26 +82,4 @@ describe('TakePortraitPhotoPanel', () => {
|
||||
fireEvent.click(button);
|
||||
expect(history.location.pathname).toEqual('/summary');
|
||||
});
|
||||
|
||||
it('shows correct text if user should use upload', async () => {
|
||||
contextValue.optimizelyExperimentName = 'test';
|
||||
contextValue.shouldUseCamera = false;
|
||||
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlTakePortraitPhotoPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
|
||||
// check that upload title and text are correct
|
||||
const title = await screen.findByText('Upload a Photo of Yourself');
|
||||
expect(title).toBeVisible();
|
||||
|
||||
const text = await screen.findByTestId('upload-text');
|
||||
expect(text.textContent).toContain('Please upload a portrait photo');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user