33 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44
55import React from 'react' ;
6- import { screen , waitFor } from '@testing-library/react' ;
6+ import { act , screen , waitFor } from '@testing-library/react' ;
77import userEvent from '@testing-library/user-event' ;
88import { mockAppContext , renderWithRouter } from '../../../models/mocks' ;
99import { MfaGuard } from './index' ;
10- import { JwtTokenCache } from '../../../lib/cache' ;
10+ import { JwtTokenCache , MfaOtpRequestCache } from '../../../lib/cache' ;
1111import { AuthUiErrors } from '../../../lib/auth-errors/auth-errors' ;
1212import { AppContext } from '../../../models' ;
1313
@@ -56,9 +56,8 @@ async function submitCode(otp: string = mockOtp) {
5656
5757describe ( 'MfaGuard' , ( ) => {
5858 beforeEach ( ( ) => {
59- if ( JwtTokenCache . hasToken ( mockSessionToken , mockScope ) ) {
60- JwtTokenCache . removeToken ( mockSessionToken , mockScope ) ;
61- }
59+ JwtTokenCache . removeToken ( mockSessionToken , mockScope ) ;
60+ MfaOtpRequestCache . remove ( mockSessionToken , mockScope ) ;
6261 jest . clearAllMocks ( ) ;
6362 } ) ;
6463
@@ -127,7 +126,7 @@ describe('MfaGuard', () => {
127126 it ( 'clears error banner on input change' , async ( ) => {
128127 renderWithRouter (
129128 < AppContext . Provider value = { mockAppContext ( ) } >
130- < MfaGuard requiredScope = { mockScope } >
129+ < MfaGuard requiredScope = { mockScope } debounceIntervalMs = { 0 } >
131130 < div > secured</ div >
132131 </ MfaGuard >
133132 </ AppContext . Provider >
@@ -149,7 +148,7 @@ describe('MfaGuard', () => {
149148 it ( 'shows resend success banner and hides error banner on resend success' , async ( ) => {
150149 renderWithRouter (
151150 < AppContext . Provider value = { mockAppContext ( ) } >
152- < MfaGuard requiredScope = { mockScope } >
151+ < MfaGuard requiredScope = { mockScope } debounceIntervalMs = { 0 } >
153152 < div > secured</ div >
154153 </ MfaGuard >
155154 </ AppContext . Provider >
@@ -179,7 +178,7 @@ describe('MfaGuard', () => {
179178 it ( 'shows error banner and hide success banner on resend error' , async ( ) => {
180179 renderWithRouter (
181180 < AppContext . Provider value = { mockAppContext ( ) } >
182- < MfaGuard requiredScope = { mockScope } >
181+ < MfaGuard requiredScope = { mockScope } debounceIntervalMs = { 0 } >
183182 < div > secured</ div >
184183 </ MfaGuard >
185184 </ AppContext . Provider >
@@ -209,7 +208,7 @@ describe('MfaGuard', () => {
209208
210209 renderWithRouter (
211210 < AppContext . Provider value = { mockAppContext ( ) } >
212- < MfaGuard requiredScope = { mockScope } >
211+ < MfaGuard requiredScope = { mockScope } debounceIntervalMs = { 0 } >
213212 < div > secured</ div >
214213 </ MfaGuard >
215214 </ AppContext . Provider >
@@ -226,7 +225,11 @@ describe('MfaGuard', () => {
226225
227226 renderWithRouter (
228227 < AppContext . Provider value = { mockAppContext ( ) } >
229- < MfaGuard requiredScope = { mockScope } onDismissCallback = { mockOnDismiss } >
228+ < MfaGuard
229+ requiredScope = { mockScope }
230+ onDismissCallback = { mockOnDismiss }
231+ debounceIntervalMs = { 0 }
232+ >
230233 < div > secured</ div >
231234 </ MfaGuard >
232235 </ AppContext . Provider >
@@ -236,4 +239,39 @@ describe('MfaGuard', () => {
236239
237240 expect ( mockOnDismiss ) . toHaveBeenCalledTimes ( 1 ) ;
238241 } ) ;
242+
243+ it ( 'debounces OTP resend requests' , async ( ) => {
244+ renderWithRouter (
245+ < AppContext . Provider value = { mockAppContext ( ) } >
246+ < MfaGuard requiredScope = { mockScope } debounceIntervalMs = { 100 } >
247+ < div > secured</ div >
248+ </ MfaGuard >
249+ </ AppContext . Provider >
250+ ) ;
251+
252+ // Should be debounced! The dialog just rendered and a code went out...
253+ await userEvent . click (
254+ screen . getByRole ( 'button' , { name : 'Email new code.' } )
255+ ) ;
256+ await act ( async ( ) => {
257+ await new Promise ( ( r ) => setTimeout ( r , 101 ) ) ;
258+ } ) ;
259+
260+ await userEvent . click (
261+ screen . getByRole ( 'button' , { name : 'Email new code.' } )
262+ ) ;
263+ // Should be debounced! The resend request above was just clicked...
264+ await userEvent . click (
265+ screen . getByRole ( 'button' , { name : 'Email new code.' } )
266+ ) ;
267+
268+ await act ( async ( ) => {
269+ await new Promise ( ( r ) => setTimeout ( r , 101 ) ) ;
270+ } ) ;
271+ await userEvent . click (
272+ screen . getByRole ( 'button' , { name : 'Email new code.' } )
273+ ) ;
274+
275+ expect ( mockAuthClient . mfaRequestOtp ) . toHaveBeenCalledTimes ( 3 ) ;
276+ } ) ;
239277} ) ;
0 commit comments