import * as React from 'react'; import { screen } from '@testing-library/react'; import { when, resetAllWhenMocks } from 'jest-when'; import { IlluminationState, FaceMatchState, LivenessErrorState, } from '../../service'; import { renderWithLivenessProvider, getMockedFunction } from '../../__mocks__'; import { useLivenessActor, useLivenessSelector } from '../../hooks'; import { Hint, selectErrorState, selectFaceMatchState, selectIlluminationState, } from '../Hint'; import { getDisplayText } from '../../utils/getDisplayText'; jest.mock('../../hooks'); jest.mock('../../hooks/useLivenessSelector'); const mockUseLivenessActor = getMockedFunction(useLivenessActor); const mockUseLivenessSelector = getMockedFunction(useLivenessSelector); describe('Hint', () => { const mockActorState: any = { matches: jest.fn(), }; const mockActorSend = jest.fn(); let errorState: LivenessErrorState | null = null; let faceMatchState: FaceMatchState | null = null; let illuminationState: IlluminationState | null = null; let faceMatchStateBeforeStart: FaceMatchState | null = null; let isFaceFarEnoughBeforeRecordingState: boolean | null = null; let isNotRecording = false; let isRecording = false; let isUploading = false; let isCheckSuccessful = false; let isCheckFailed = false; let isCheckFaceDetectedBeforeStart = false; let isCheckFaceDistanceBeforeRecording = false; let isWaitingForSessionInfo = false; let isFlashingFreshness = false; const { hintDisplayText } = getDisplayText(undefined); function mockStateMatchesAndSelectors() { mockUseLivenessSelector .mockReturnValueOnce(errorState) .mockReturnValueOnce(faceMatchState) .mockReturnValueOnce(illuminationState) .mockReturnValueOnce(faceMatchStateBeforeStart) .mockReturnValueOnce(isFaceFarEnoughBeforeRecordingState); when(mockActorState.matches) .calledWith('notRecording') .mockReturnValue(isNotRecording) .calledWith('recording') .mockReturnValue(isRecording) .calledWith('uploading') .mockReturnValue(isUploading) .calledWith('checkSucceeded') .mockReturnValue(isCheckSuccessful) .calledWith('checkFailed') .mockReturnValue(isCheckFailed) .calledWith('checkFaceDetectedBeforeStart') .mockReturnValue(isCheckFaceDetectedBeforeStart) .calledWith('checkFaceDistanceBeforeRecording') .mockReturnValue(isCheckFaceDistanceBeforeRecording) .calledWith('waitForSessionInfo') .mockReturnValue(isWaitingForSessionInfo) .calledWith({ recording: 'flashFreshnessColors' }) .mockReturnValue(isFlashingFreshness); } beforeEach(() => { mockUseLivenessActor.mockReturnValue([mockActorState, mockActorSend]); }); afterEach(() => { errorState = null; faceMatchState = null; illuminationState = null; faceMatchStateBeforeStart = null; isFaceFarEnoughBeforeRecordingState = null; isNotRecording = false; isRecording = false; isUploading = false; isCheckSuccessful = false; isCheckFailed = false; isCheckFaceDetectedBeforeStart = false; isCheckFaceDistanceBeforeRecording = false; isWaitingForSessionInfo = false; isFlashingFreshness = false; jest.clearAllMocks(); resetAllWhenMocks(); }); it('should render nothing if error', () => { errorState = LivenessErrorState.FACE_DISTANCE_ERROR; mockStateMatchesAndSelectors(); renderWithLivenessProvider(); expect( screen.queryByText(hintDisplayText.hintHoldFacePositionCountdownText) ).not.toBeInTheDocument(); expect( screen.queryByText(hintDisplayText.hintMoveFaceFrontOfCameraText) ).not.toBeInTheDocument(); }); it('should render nothing if failed', () => { isCheckFailed = true; mockStateMatchesAndSelectors(); renderWithLivenessProvider(); expect( screen.queryByText(hintDisplayText.hintHoldFacePositionCountdownText) ).not.toBeInTheDocument(); expect( screen.queryByText(hintDisplayText.hintMoveFaceFrontOfCameraText) ).not.toBeInTheDocument(); }); it('should render hint to move only one face onto camera view if isCheckFaceDetectedBeforeStart is true', () => { isCheckFaceDetectedBeforeStart = true; mockStateMatchesAndSelectors(); renderWithLivenessProvider(); expect( screen.getByText(hintDisplayText.hintMoveFaceFrontOfCameraText) ).toBeInTheDocument(); }); it('should render hint to about too many faces faceMatchStateBeforeStart is TOO_MANY', () => { isCheckFaceDetectedBeforeStart = true; faceMatchStateBeforeStart = FaceMatchState.TOO_MANY; mockStateMatchesAndSelectors(); renderWithLivenessProvider(); expect( screen.getByText(hintDisplayText.hintTooManyFacesText) ).toBeInTheDocument(); }); it('should render hint to move face further away if checking face distance before recording is false', () => { isCheckFaceDistanceBeforeRecording = true; isFaceFarEnoughBeforeRecordingState = false; mockStateMatchesAndSelectors(); renderWithLivenessProvider(); expect( screen.getByText(hintDisplayText.hintTooCloseText) ).toBeInTheDocument(); }); it('should render hold face in oval message if flashing freshness colors', () => { isFlashingFreshness = true; mockStateMatchesAndSelectors(); renderWithLivenessProvider(); expect( screen.getByText(hintDisplayText.hintHoldFaceForFreshnessText) ).toBeInTheDocument(); }); it('should render not recording state if present', () => { isNotRecording = true; mockStateMatchesAndSelectors(); renderWithLivenessProvider(); expect( screen.getByText(hintDisplayText.hintConnectingText) ).toBeInTheDocument(); }); it('should render uploading state if present', () => { isUploading = true; mockStateMatchesAndSelectors(); renderWithLivenessProvider(); expect( screen.getByText(hintDisplayText.hintVerifyingText) ).toBeInTheDocument(); }); it('should not render check succeeded state if present', () => { isCheckSuccessful = true; mockStateMatchesAndSelectors(); renderWithLivenessProvider(); expect(screen.queryByText('Check successful')).not.toBeInTheDocument(); }); it('should render illumination state if present', () => { illuminationState = IlluminationState.DARK; mockStateMatchesAndSelectors(); renderWithLivenessProvider(); expect( screen.getByText(hintDisplayText.hintIlluminationTooDarkText) ).toBeInTheDocument(); }); it('should not render illumination state if NORMAL', () => { illuminationState = IlluminationState.NORMAL; mockStateMatchesAndSelectors(); renderWithLivenessProvider(); expect( screen.queryByText(hintDisplayText.hintIlluminationNormalText) ).not.toBeInTheDocument(); }); it('should render TOO_CLOSE text if faceMatchState = TOO_CLOSE and recording', () => { faceMatchState = FaceMatchState.TOO_CLOSE; isRecording = true; mockStateMatchesAndSelectors(); renderWithLivenessProvider(); expect( screen.getByText(hintDisplayText.hintTooCloseText) ).toBeInTheDocument(); }); it('should render TOO_FAR text if faceMatchState = TOO_FAR and recording', () => { faceMatchState = FaceMatchState.TOO_FAR; isRecording = true; mockStateMatchesAndSelectors(); renderWithLivenessProvider(); expect( screen.getByText(hintDisplayText.hintTooFarText) ).toBeInTheDocument(); }); it('should render TOO_FAR text if faceMatchState = CANT_IDENTIFY and recording', () => { faceMatchState = FaceMatchState.CANT_IDENTIFY; isRecording = true; mockStateMatchesAndSelectors(); renderWithLivenessProvider(); expect( screen.getByText(hintDisplayText.hintTooFarText) ).toBeInTheDocument(); }); it('should render TOO_FAR text if faceMatchState = FACE_IDENTIFIED and recording', () => { faceMatchState = FaceMatchState.FACE_IDENTIFIED; isRecording = true; mockStateMatchesAndSelectors(); renderWithLivenessProvider(); expect( screen.getByText(hintDisplayText.hintTooFarText) ).toBeInTheDocument(); }); it('should create appropriate selectors', () => { const expectedErrorState = LivenessErrorState.RUNTIME_ERROR; const expectedFaceMatchState = FaceMatchState.TOO_CLOSE; const expectedIlluminationState = IlluminationState.DARK; const state: any = { context: { errorState: expectedErrorState, faceMatchAssociatedParams: { faceMatchState: expectedFaceMatchState, illuminationState: expectedIlluminationState, }, }, }; const actualErrorState = selectErrorState(state); const actualFaceMatchState = selectFaceMatchState(state); const actualIlluminationState = selectIlluminationState(state); expect(actualErrorState).toEqual(expectedErrorState); expect(actualFaceMatchState).toEqual(expectedFaceMatchState); expect(actualIlluminationState).toEqual(expectedIlluminationState); }); });