Skip to content

Commit

Permalink
Add JS tests for the VerifyPhoneNumberContent component.
Browse files Browse the repository at this point in the history
  • Loading branch information
eason9487 committed Jul 31, 2023
1 parent 754da6e commit a4f41dd
Showing 1 changed file with 311 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
/**
* External dependencies
*/
import '@testing-library/jest-dom';
import { screen, render, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
// import { parsePhoneNumberFromString as parsePhoneNumber } from 'libphonenumber-js';

/**
* Internal dependencies
*/
import VerifyPhoneNumberContent from './verify-phone-number-content';
import { useAppDispatch } from '.~/data';

jest.mock( '.~/data', () => ( {
...jest.requireActual( '.~/data' ),
useAppDispatch: jest.fn(),
} ) );

describe( 'VerifyPhoneNumberContent', () => {
let requestPhoneVerificationCode;
let verifyPhoneNumber;

function getSwitchButton() {
return screen.getByRole( 'button', {
name: 'Switch verification method',
} );
}

function getVerifyButton() {
return screen.getByRole( 'button', { name: /Verify/ } );
}

beforeEach( () => {
requestPhoneVerificationCode = jest
.fn( () => Promise.resolve( { verificationId: '987654321' } ) )
.mockName( 'requestPhoneVerificationCode' );

verifyPhoneNumber = jest
.fn( () => {
return new Promise( ( resolve, reject ) => {
setTimeout(
() => reject( { display: 'error reason: JS test' } ),
1000
);
} );
} )
.mockName( 'verifyPhoneNumber' );

useAppDispatch.mockReturnValue( {
requestPhoneVerificationCode,
verifyPhoneNumber,
} );
} );

it( 'Should render the given props with corresponding verification method', () => {
render(
<VerifyPhoneNumberContent
verificationMethod="SMS"
country="US"
number="+12133734253"
display="+1 213 373 4253"
/>
);

const switchButton = getSwitchButton();

expect( switchButton ).toBeInTheDocument();
expect( switchButton.textContent ).toBe(
'Or, receive a verification code through a phone call'
);
expect( screen.getByText( '+1 213 373 4253' ) ).toBeInTheDocument();
expect(
screen.getByText(
/A text message with the 6-digit verification code has been sent/
)
).toBeInTheDocument();
expect(
screen.getByRole( 'button', { name: /Resend code/ } )
).toBeInTheDocument();

expect( requestPhoneVerificationCode ).toHaveBeenCalledTimes( 1 );

// Switch to the PHONE_CALL method
userEvent.click( switchButton );

expect( switchButton.textContent ).toBe(
'Or, receive a verification code through text message'
);
expect( screen.getByText( '+1 213 373 4253' ) ).toBeInTheDocument();
expect(
screen.getByText( /You will receive a phone call/ )
).toBeInTheDocument();
expect(
screen.getByRole( 'button', { name: /Call again/ } )
).toBeInTheDocument();

expect( requestPhoneVerificationCode ).toHaveBeenCalledTimes( 2 );
} );

it( 'When not yet entered 6-digit verification code, should disable the "Verify phone number" button', async () => {
await act( async () => {
render(
<VerifyPhoneNumberContent
verificationMethod="SMS"
country="US"
number="+12133734253"
display="+1 213 373 4253"
/>
);
} );

const button = getVerifyButton();

expect( button ).toBeInTheDocument();
expect( button ).toBeDisabled();

screen.getAllByRole( 'textbox' ).forEach( ( codeInput, i ) => {
userEvent.type( codeInput, i.toString() );
} );

expect( button ).toBeEnabled();
} );

it( 'When waiting for the countdown of each verification method, should disable the "Resend code/Call again" button', () => {
render(
<VerifyPhoneNumberContent
verificationMethod="SMS"
country="US"
number="+12133734253"
display="+1 213 373 4253"
/>
);

const button = screen.getByRole( 'button', { name: /Resend code/ } );

expect( button ).toBeDisabled();

// Switch to the PHONE_CALL method
userEvent.click( getSwitchButton() );

expect( button ).toBeDisabled();
} );

it( 'When not waiting for the countdown of each verification method, should call `requestPhoneVerificationCode` with the country code, phone number and verification method', () => {
render(
<VerifyPhoneNumberContent
verificationMethod="SMS"
country="US"
number="+12133734253"
display="+1 213 373 4253"
/>
);

const switchButton = getSwitchButton();

expect( requestPhoneVerificationCode ).toHaveBeenCalledTimes( 1 );
expect( requestPhoneVerificationCode ).toHaveBeenCalledWith(
'US',
'+12133734253',
'SMS'
);

// Switch to the PHONE_CALL method
userEvent.click( switchButton );

expect( requestPhoneVerificationCode ).toHaveBeenCalledTimes( 2 );
expect( requestPhoneVerificationCode ).toHaveBeenLastCalledWith(
'US',
'+12133734253',
'PHONE_CALL'
);

// Switch back to the SMS method but it's still waiting for countdown
userEvent.click( switchButton );

expect( requestPhoneVerificationCode ).toHaveBeenCalledTimes( 2 );

// Switch back to the PHONE_CALL method but it's still waiting for countdown
userEvent.click( switchButton );

expect( requestPhoneVerificationCode ).toHaveBeenCalledTimes( 2 );
} );

it( 'When clicking the "Verify phone number" button, should call `verifyPhoneNumber` with the verification id, code and method', async () => {
await act( async () => {
render(
<VerifyPhoneNumberContent
verificationMethod="SMS"
country="US"
number="+12133734253"
display="+1 213 373 4253"
onVerificationStateChange={ () => {} }
/>
);
} );

const button = getVerifyButton();

screen.getAllByRole( 'textbox' ).forEach( ( codeInput, i ) => {
userEvent.type( codeInput, i.toString() );
} );

expect( verifyPhoneNumber ).toHaveBeenCalledTimes( 0 );

userEvent.click( button );

expect( verifyPhoneNumber ).toHaveBeenCalledTimes( 1 );
expect( verifyPhoneNumber ).toHaveBeenLastCalledWith(
'987654321',
'012345',
'SMS'
);
} );

it( 'Should call back to `onVerificationStateChange` according to the state and result of `verifyPhoneNumber`', async () => {
const onVerificationStateChange = jest
.fn()
.mockName( 'onVerificationStateChange' );

await act( async () => {
render(
<VerifyPhoneNumberContent
verificationMethod="SMS"
country="US"
number="+12133734253"
display="+1 213 373 4253"
onVerificationStateChange={ onVerificationStateChange }
/>
);
} );

const button = getVerifyButton();

screen.getAllByRole( 'textbox' ).forEach( ( codeInput, i ) => {
userEvent.type( codeInput, i.toString() );
} );

// -----------------------------------------
// Failed `verifyPhoneNumber` calling result
// -----------------------------------------
expect( onVerificationStateChange ).toHaveBeenCalledTimes( 0 );

await userEvent.click( button );

// First callback for loading state:
// 1. isVerifying: true
// 2. isVerified: false
expect( onVerificationStateChange ).toHaveBeenCalledTimes( 1 );
expect( onVerificationStateChange ).toHaveBeenCalledWith( true, false );

await act( async () => {
jest.runOnlyPendingTimers();
} );

// Second callback for failed result:
// 1. isVerifying: false
// 2. isVerified: false
expect( onVerificationStateChange ).toHaveBeenCalledTimes( 2 );
expect( onVerificationStateChange ).toHaveBeenLastCalledWith(
false,
false
);

// `Notice` component will insert another invisible text element under <body> with the same string.
// So here filter out it.
expect(
screen.getByText( ( content, element ) => {
return (
element.parentElement.tagName !== 'BODY' &&
content === 'error reason: JS test'
);
} )
).toBeInTheDocument();

// ---------------------------------------------
// Successful `verifyPhoneNumber` calling result
// ---------------------------------------------
onVerificationStateChange.mockReset();

verifyPhoneNumber.mockImplementation(
() => new Promise( ( resolve ) => setTimeout( resolve, 1000 ) )
);

expect( onVerificationStateChange ).toHaveBeenCalledTimes( 0 );

await userEvent.click( button );

// First callback for loading state:
// 1. isVerifying: true
// 2. isVerified: false
expect( onVerificationStateChange ).toHaveBeenCalledTimes( 1 );
expect( onVerificationStateChange ).toHaveBeenCalledWith( true, false );

await act( async () => {
jest.runOnlyPendingTimers();
} );

// Second callback for successful result:
// 1. isVerifying: false
// 2. isVerified: true
expect( onVerificationStateChange ).toHaveBeenCalledTimes( 2 );
expect( onVerificationStateChange ).toHaveBeenLastCalledWith(
false,
true
);

// The verify button should keep disabled after successfully verified
expect( button ).toBeDisabled();
} );
} );

0 comments on commit a4f41dd

Please sign in to comment.