@@ -9,10 +9,12 @@ import {
99} from "@bitwarden/auth/common" ;
1010import { ApiService } from "@bitwarden/common/abstractions/api.service" ;
1111import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result" ;
12+ import { ErrorResponse } from "@bitwarden/common/models/response/error.response" ;
1213import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service" ;
1314import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service" ;
1415import { LogService } from "@bitwarden/common/platform/abstractions/log.service" ;
1516import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service" ;
17+ import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service" ;
1618import { ToastService } from "@bitwarden/components" ;
1719import { KeyService } from "@bitwarden/key-management" ;
1820import { I18nPipe } from "@bitwarden/ui-common" ;
@@ -34,6 +36,7 @@ describe("RecoverTwoFactorComponent", () => {
3436 let mockConfigService : MockProxy < ConfigService > ;
3537 let mockLoginSuccessHandlerService : MockProxy < LoginSuccessHandlerService > ;
3638 let mockLogService : MockProxy < LogService > ;
39+ let mockValidationService : MockProxy < ValidationService > ;
3740
3841 beforeEach ( ( ) => {
3942 mockRouter = mock < Router > ( ) ;
@@ -46,6 +49,7 @@ describe("RecoverTwoFactorComponent", () => {
4649 mockConfigService = mock < ConfigService > ( ) ;
4750 mockLoginSuccessHandlerService = mock < LoginSuccessHandlerService > ( ) ;
4851 mockLogService = mock < LogService > ( ) ;
52+ mockValidationService = mock < ValidationService > ( ) ;
4953
5054 TestBed . configureTestingModule ( {
5155 declarations : [ RecoverTwoFactorComponent ] ,
@@ -60,6 +64,7 @@ describe("RecoverTwoFactorComponent", () => {
6064 { provide : ConfigService , useValue : mockConfigService } ,
6165 { provide : LoginSuccessHandlerService , useValue : mockLoginSuccessHandlerService } ,
6266 { provide : LogService , useValue : mockLogService } ,
67+ { provide : ValidationService , useValue : mockValidationService } ,
6368 ] ,
6469 imports : [ I18nPipe ] ,
6570 // FIXME(PM-18598): Replace unknownElements and unknownProperties with actual imports
@@ -70,16 +75,21 @@ describe("RecoverTwoFactorComponent", () => {
7075 component = fixture . componentInstance ;
7176 } ) ;
7277
73- afterEach ( ( ) => {
74- jest . resetAllMocks ( ) ;
75- } ) ;
76-
7778 describe ( "handleRecoveryLogin" , ( ) => {
79+ let email : string ;
80+ let recoveryCode : string ;
81+
82+ beforeEach ( ( ) => {
83+ 84+ recoveryCode = "testRecoveryCode" ;
85+ } ) ;
86+
87+ afterEach ( ( ) => {
88+ jest . resetAllMocks ( ) ;
89+ } ) ;
90+
7891 it ( "should log in successfully and navigate to the two-factor settings page" , async ( ) => {
7992 // Arrange
80- const email = "[email protected] " ; 81- const recoveryCode = "testRecoveryCode" ;
82-
8393 const authResult = new AuthResult ( ) ;
8494 mockLoginStrategyService . logIn . mockResolvedValue ( authResult ) ;
8595
@@ -98,14 +108,16 @@ describe("RecoverTwoFactorComponent", () => {
98108 expect ( mockRouter . navigate ) . toHaveBeenCalledWith ( [ "/settings/security/two-factor" ] ) ;
99109 } ) ;
100110
101- it ( "should handle login errors and redirect to login page " , async ( ) => {
111+ it ( "should log an error and set an inline error on the recoveryCode form control upon receiving an ErrorResponse due to an invalid token " , async ( ) => {
102112 // Arrange
103- const email = "[email protected] " ; 104- const recoveryCode = "testRecoveryCode" ;
105-
106- const error = new Error ( "Login failed" ) ;
113+ const error = new ErrorResponse ( "mockError" , 400 ) ;
114+ error . message = "Two-step token is invalid" ;
107115 mockLoginStrategyService . logIn . mockRejectedValue ( error ) ;
108116
117+ const recoveryCodeControl = component . formGroup . get ( "recoveryCode" ) ;
118+ jest . spyOn ( recoveryCodeControl , "setErrors" ) ;
119+ mockI18nService . t . mockReturnValue ( "Invalid recovery code" ) ;
120+
109121 // Act
110122 await component [ "loginWithRecoveryCode" ] ( email , recoveryCode ) ;
111123
@@ -114,9 +126,43 @@ describe("RecoverTwoFactorComponent", () => {
114126 "Error logging in automatically: " ,
115127 error . message ,
116128 ) ;
117- expect ( mockRouter . navigate ) . toHaveBeenCalledWith ( [ "/login" ] , {
118- queryParams : { email : email } ,
129+ expect ( recoveryCodeControl . setErrors ) . toHaveBeenCalledWith ( {
130+ invalidRecoveryCode : { message : "Invalid recovery code" } ,
119131 } ) ;
120132 } ) ;
133+
134+ it ( "should log an error and show validation but not set an inline error on the recoveryCode form control upon receiving some other ErrorResponse" , async ( ) => {
135+ // Arrange
136+ const error = new ErrorResponse ( "mockError" , 400 ) ;
137+ error . message = "Some other error" ;
138+ mockLoginStrategyService . logIn . mockRejectedValue ( error ) ;
139+
140+ const recoveryCodeControl = component . formGroup . get ( "recoveryCode" ) ;
141+ jest . spyOn ( recoveryCodeControl , "setErrors" ) ;
142+
143+ // Act
144+ await component [ "loginWithRecoveryCode" ] ( email , recoveryCode ) ;
145+
146+ // Assert
147+ expect ( mockLogService . error ) . toHaveBeenCalledWith (
148+ "Error logging in automatically: " ,
149+ error . message ,
150+ ) ;
151+ expect ( mockValidationService . showError ) . toHaveBeenCalledWith ( error . message ) ;
152+ expect ( recoveryCodeControl . setErrors ) . not . toHaveBeenCalled ( ) ;
153+ } ) ;
154+
155+ it ( "should log an error and show validation upon receiving a non-ErrorResponse error" , async ( ) => {
156+ // Arrange
157+ const error = new Error ( "Generic error" ) ;
158+ mockLoginStrategyService . logIn . mockRejectedValue ( error ) ;
159+
160+ // Act
161+ await component [ "loginWithRecoveryCode" ] ( email , recoveryCode ) ;
162+
163+ // Assert
164+ expect ( mockLogService . error ) . toHaveBeenCalledWith ( "Error logging in automatically: " , error ) ;
165+ expect ( mockValidationService . showError ) . toHaveBeenCalledWith ( error ) ;
166+ } ) ;
121167 } ) ;
122168} ) ;
0 commit comments