diff --git a/src/app/client/src/app/modules/public/module/sign-in/sso/components/update-contact/update-contact.component.spec.ts b/src/app/client/src/app/modules/public/module/sign-in/sso/components/update-contact/update-contact.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3c5237a7a8516f43e032667cf05eded9c6a36f69
--- /dev/null
+++ b/src/app/client/src/app/modules/public/module/sign-in/sso/components/update-contact/update-contact.component.spec.ts
@@ -0,0 +1,399 @@
+import { FormControl, FormGroup } from '@angular/forms';
+import { UpdateContactComponent } from './update-contact.component';
+import { ActivatedRoute } from '@angular/router';
+import { ToasterService } from '@sunbird/shared';
+import { TncService } from '@sunbird/core';
+import { TelemetryService } from '@sunbird/telemetry';
+import { ResourceService } from '@sunbird/shared';
+import { of as observableOf, Observable, throwError as observableThrowError } from 'rxjs';
+import { TenantService, UserService, OtpService, OrgDetailsService } from '@sunbird/core';
+import { mockUpdateContactData } from './update-contact.mock.spec.data'
+import { NavigationHelperService, UtilService } from '../../../../../../shared/services';
+import { SignupService } from '../../../../signup';
+
+describe('UpdateContactComponent', () => {
+    let component: UpdateContactComponent;
+
+    const activatedRoute: Partial<ActivatedRoute> = {
+        snapshot: mockUpdateContactData.snapshotData as any,
+        queryParams: observableOf({ userId: 'mockUserId' })
+    };
+    const tenantService: Partial<TenantService> = {
+        tenantData$: observableOf({ favicon: 'sample-favicon', tenantData: { logo: 'test' } }) as any
+    };
+    const resourceService: Partial<ResourceService> = {
+        frmelmnts: mockUpdateContactData.resourceBundle.frmelmnts,
+        messages: mockUpdateContactData.resourceBundle.messages
+    };
+    const userService: Partial<UserService> = {
+        getUserByKey: jest.fn().mockReturnValue(observableOf(mockUpdateContactData.userData))
+    };
+    const otpService: Partial<OtpService> = {
+        generateOTP: jest.fn().mockReturnValue(observableOf())
+    };
+    const toasterService: Partial<ToasterService> = {
+        error: jest.fn()
+    };
+    const navigationHelperService: Partial<NavigationHelperService> = {
+        getPageLoadTime: jest.fn()
+    };
+    const orgDetailsService: Partial<OrgDetailsService> = {
+        getCustodianOrgDetails: jest.fn()
+    };
+    const utilService: Partial<UtilService> = {
+        parseJson: jest.fn()
+    };
+    const signupService: Partial<SignupService> = {};
+    const telemetryService: Partial<TelemetryService> = {
+        interact: jest.fn(),
+        log: jest.fn()
+    };
+    const tncService: Partial<TncService> = {
+        getTncConfig: jest.fn().mockReturnValue(observableOf({
+            'id': 'api',
+            'params': {
+                'status': 'success',
+            },
+            'responseCode': 'OK',
+            'result': {
+                'response': {
+                    'id': 'tncConfig',
+                    'field': 'tncConfig',
+                    'value': '{"latestVersion":"v4","v4":{"url":}}'
+                }
+            }
+        }))
+    };
+
+    beforeEach(() => {
+        component = new UpdateContactComponent(
+
+            activatedRoute as ActivatedRoute,
+            tenantService as TenantService,
+            resourceService as ResourceService,
+            userService as UserService,
+            otpService as OtpService,
+            toasterService as ToasterService,
+            navigationHelperService as NavigationHelperService,
+            orgDetailsService as OrgDetailsService,
+            utilService as UtilService,
+            signupService as SignupService,
+            telemetryService as TelemetryService,
+            tncService as TncService,
+        )
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+
+    it('should show Merge confirmation after success otp verification', () => {
+        component.contactForm.type = 'email';
+        component.contactForm.value = 'test@gmail.com';
+        component.userDetails = {
+            id: 'mockId'
+        };
+        component.handleOtpValidationSuccess();
+        expect(component.showOtpComp).toEqual(false);
+        expect(component.showMergeConfirmation).toEqual(true);
+    });
+
+    it('should redirect verified user', () => {
+        jest.spyOn(component, 'getQueryParams');
+        Object.defineProperty(window, 'location', {
+            writable: true,
+            value: { assign: jest.fn() }
+        });
+        component.userDetails = {};
+        component.handleOtpValidationSuccess();
+        expect(component.getQueryParams).toHaveBeenCalled();
+    });
+
+    it('should reset form as otp validation failed', () => {
+        component.handleOtpValidationFailed();
+        expect(component).toBeTruthy();
+        expect(component.showOtpComp).toEqual(false);
+        expect(component.disableSubmitBtn).toEqual(true);
+        expect(component.contactForm).toEqual({
+            value: '',
+            type: 'phone',
+            tncAccepted: false
+        });
+        expect(component.userExist).toEqual(false);
+        expect(component.userBlocked).toEqual(false);
+    });
+
+    it('should reset form as otp validation failed', (done) => {
+        jest.spyOn(component, 'handleFormChangeEvent').mockImplementation();
+        component.handleOtpValidationFailed();
+        setTimeout(() => {
+            expect(component.handleFormChangeEvent).toHaveBeenCalled();
+            done()
+        }, 100);
+    });
+
+    it('should return query params for given query object', () => {
+        const test = component.getQueryParams({ queryParam1: 'mockValue' });
+        expect(test).toEqual('?queryParam1=mockValue');
+    });
+
+    it('should not fail if query parmas are not send', () => {
+        const test = component.getQueryParams({});
+        expect(test).toEqual('?');
+    });
+
+    it('should reset form', () => {
+        component.resetForm();
+        expect(component.disableSubmitBtn).toEqual(true);
+        expect(component.contactForm).toEqual({
+            value: '',
+            type: 'phone',
+            tncAccepted: false
+        });
+        expect(component.userExist).toEqual(false);
+        expect(component.userDetails).toEqual({});
+        expect(component.userBlocked).toEqual(false);
+    });
+
+    it('should not throw error and not generate otp as root org id is different', () => {
+        component.contactForm.type = 'email';
+        component.contactForm.value = 'test@gmail.com';
+        jest.spyOn(userService, 'getUserByKey').mockImplementation(() => observableOf(mockUpdateContactData.userData) as any);
+        jest.spyOn(orgDetailsService, 'getCustodianOrgDetails').mockImplementation(() => observableOf(mockUpdateContactData.nonCustOrgDetails) as any);
+        component.onFormUpdate();
+        expect(component.userExist).toEqual(true);
+        expect(component.userBlocked).toEqual(false);
+        expect(component.disableSubmitBtn).toEqual(true);
+    });
+
+    it('should not generate otp as user account blocked', () => {
+        component.contactForm.type = 'email';
+        component.contactForm.value = 'test@gmail.com';
+        jest.spyOn(userService, 'getUserByKey').mockImplementation(() => observableThrowError(mockUpdateContactData.blockedUserError));
+        jest.spyOn(orgDetailsService, 'getCustodianOrgDetails').mockImplementation(() => observableOf(mockUpdateContactData.nonCustOrgDetails));
+        component.onFormUpdate();
+        expect(component.userExist).toEqual(false);
+        expect(component.userBlocked).toEqual(true);
+        expect(component.disableSubmitBtn).toEqual(true);
+    });
+
+    it('should not generate otp as an eror occured', () => {
+        component.contactForm.type = 'email';
+        component.contactForm.value = 'test@gmail.com';
+        jest.spyOn(userService, 'getUserByKey').mockImplementation(() => observableThrowError({
+            error: {
+                params: {
+                    status: 'I\'m a teapot'
+                }
+            }
+        }));
+        jest.spyOn(orgDetailsService, 'getCustodianOrgDetails').mockImplementation(() => observableOf(mockUpdateContactData.nonCustOrgDetails));
+        component.onFormUpdate();
+        expect(component.disableSubmitBtn).toEqual(true);
+        expect(component.captchaValidationFailed).toEqual(true);
+    });
+
+    it('should generate otp as user not found', () => {
+        component.contactForm.type = 'email';
+        component.contactForm.value = 'test@gmail.com';
+        jest.spyOn(userService, 'getUserByKey').mockImplementation(() => observableThrowError(mockUpdateContactData.serverError) as any);
+        jest.spyOn(orgDetailsService, 'getCustodianOrgDetails').mockImplementation(() =>
+            observableOf(mockUpdateContactData.nonCustOrgDetails));
+        jest.spyOn(otpService, 'generateOTP').mockImplementation(() => observableOf(mockUpdateContactData.successResponse) as any);
+        component.onFormUpdate();
+    });
+
+    it('should not generate otp and throw error as phone is in use', () => {
+        component.contactForm.type = 'email';
+        component.contactForm.value = 'test@gmail.com';
+        jest.spyOn(userService, 'getUserByKey').mockImplementation(() => observableThrowError(mockUpdateContactData.serverError));
+        jest.spyOn(orgDetailsService, 'getCustodianOrgDetails').mockImplementation(() =>
+            observableOf(mockUpdateContactData.nonCustOrgDetails));
+        jest.spyOn(otpService, 'generateOTP').mockImplementation(() => observableThrowError(mockUpdateContactData.phoneAlreadyUsedError));
+        jest.spyOn(toasterService, 'error').mockImplementation((value) => {
+            return value;
+        });
+        component.onFormUpdate();
+        expect(toasterService.error).toHaveBeenCalledWith('PHONE_ALREADY_IN_USE');
+    });
+
+    it('should not generate otp and throw error as email is in use', () => {
+        component.contactForm.type = 'email';
+        component.contactForm.value = 'test@gmail.com';
+        jest.spyOn(userService, 'getUserByKey').mockImplementation(() => observableThrowError(mockUpdateContactData.serverError));
+        jest.spyOn(orgDetailsService, 'getCustodianOrgDetails').mockImplementation(() =>
+            observableOf(mockUpdateContactData.nonCustOrgDetails));
+        jest.spyOn(otpService, 'generateOTP').mockImplementation(() => observableThrowError(mockUpdateContactData.emailAlreadyUsedError));
+        jest.spyOn(toasterService, 'error').mockImplementation((value) => {
+            return value;
+        });
+        component.onFormUpdate();
+        expect(toasterService.error).toHaveBeenCalledWith('EMAIL_IN_USE');
+    });
+
+    it('should not generate otp and throw error as rate limit has exceed', () => {
+        component.contactForm.type = 'email';
+        component.contactForm.value = 'test@gmail.com';
+        jest.spyOn(userService, 'getUserByKey').mockImplementation(() => observableThrowError(mockUpdateContactData.serverError));
+        jest.spyOn(orgDetailsService, 'getCustodianOrgDetails').mockImplementation(() =>
+            observableOf(mockUpdateContactData.nonCustOrgDetails));
+        jest.spyOn(otpService, 'generateOTP').mockImplementation(() => observableThrowError(mockUpdateContactData.rateLimitExceed));
+        jest.spyOn(toasterService, 'error').mockImplementation((value) => {
+            return value;
+        });
+        component.onFormUpdate();
+        expect(toasterService.error).toHaveBeenCalledWith('ERROR_RATE_LIMIT_EXCEEDED');
+    });
+
+    it('should not generate otp and throw error as server error', () => {
+        component.contactForm.type = 'email';
+        component.contactForm.value = 'test@gmail.com';
+        jest.spyOn(userService, 'getUserByKey').mockImplementation(() => observableThrowError(mockUpdateContactData.serverError));
+        jest.spyOn(orgDetailsService, 'getCustodianOrgDetails').mockImplementation(() =>
+            observableOf(mockUpdateContactData.nonCustOrgDetails));
+        jest.spyOn(otpService, 'generateOTP').mockImplementation(() => observableThrowError(mockUpdateContactData.serverError));
+        jest.spyOn(toasterService, 'error').mockImplementation((value) => {
+            return value;
+        });
+        component.onFormUpdate();
+        expect(toasterService.error).toHaveBeenCalledWith(mockUpdateContactData.resourceBundle.messages.fmsg.m0085);
+    });
+
+    it('should generate otp as user org id is same for email id', () => {
+        component.contactForm.type = 'email';
+        component.contactForm.value = 'test@gmail.com';
+        jest.spyOn(userService, 'getUserByKey').mockImplementation(() => observableOf(mockUpdateContactData.userData) as Observable<any>);
+        jest.spyOn(orgDetailsService, 'getCustodianOrgDetails').mockImplementation(() =>
+            observableOf(mockUpdateContactData.custOrgDetails));
+        jest.spyOn(otpService, 'generateOTP').mockImplementation(() => observableOf(mockUpdateContactData.successResponse) as Observable<any>);
+        component.onFormUpdate();
+        expect(component.userDetails).toEqual(mockUpdateContactData.userData.result.response);
+        expect(component.userExist).toEqual(false);
+        expect(component.userBlocked).toEqual(false);
+        expect(component.disableSubmitBtn).toEqual(false);
+    });
+
+    it('should generate otp as user org id is same for phone number', () => {
+        component.contactForm.type = 'phone';
+        component.contactForm.value = '7896541257';
+        jest.spyOn(userService, 'getUserByKey').mockImplementation(() =>
+            observableOf(mockUpdateContactData.userData) as Observable<any>);
+        jest.spyOn(orgDetailsService, 'getCustodianOrgDetails').mockImplementation(() =>
+            observableOf(mockUpdateContactData.custOrgDetails));
+        jest.spyOn(otpService, 'generateOTP').mockImplementation(() =>
+            observableOf(mockUpdateContactData.successResponse) as Observable<any>);
+        component.onFormUpdate();
+        expect(component.userDetails).toEqual(mockUpdateContactData.userData.result.response);
+        expect(component.userExist).toEqual(false);
+        expect(component.userBlocked).toEqual(false);
+        expect(component.disableSubmitBtn).toEqual(false);
+    });
+
+    it('initiate instance with SUNBIRD', () => {
+        jest.spyOn(component, 'fetchTncConfiguration');
+        component.ngOnInit();
+        expect(component.instance).toEqual('SUNBIRD');
+        expect(component.fetchTncConfiguration).toHaveBeenCalled();
+    });
+
+    it('initiate instance with SUNBIRD without captcha', () => {
+        jest.spyOn(component, 'fetchTncConfiguration');
+        let mockElement = (<HTMLInputElement>document.createElement('p1reCaptchaEnabled'));
+        mockElement.value = "false";
+        jest.spyOn(document, 'getElementById').mockReturnValue(mockElement);
+        component.ngOnInit();
+        expect(component.instance).toEqual('SUNBIRD');
+        expect(component.fetchTncConfiguration).toHaveBeenCalled();
+    });
+
+    it('should toggle tnc checkboc if already false', () => {
+        jest.spyOn(telemetryService, 'interact');
+        component.toggleTncCheckBox({ target: { checked: true } });
+        expect(component.contactForm.tncAccepted).toEqual(true);
+        expect(telemetryService.interact).toHaveBeenCalledWith(mockUpdateContactData.interactEDataSelected);
+    });
+
+    it('should toggle tnc checkboc', () => {
+        jest.spyOn(telemetryService, 'interact');
+        component.toggleTncCheckBox({ target: { checked: false } });
+        expect(component.contactForm.tncAccepted).toEqual(false);
+        expect(telemetryService.interact).toHaveBeenCalledWith(mockUpdateContactData.interactEDataUnSelected);
+    });
+
+    it('should fetch tnc configuration', () => {
+        jest.spyOn(tncService, 'getTncConfig').mockReturnValue(observableOf(mockUpdateContactData.tncConfig) as any)
+        jest.spyOn(telemetryService, 'log');
+        component.fetchTncConfiguration();
+        expect(telemetryService.log).toHaveBeenCalledWith(mockUpdateContactData.telemetryLogSuccess);
+    });
+
+    it('should fetch tnc configuration and throw error as cannot parse data', () => {
+        jest.spyOn(tncService, 'getTncConfig').mockReturnValue(observableOf(mockUpdateContactData.tncConfigIncorrectData) as any);
+        jest.spyOn(toasterService, 'error');
+        component.fetchTncConfiguration();
+        expect(toasterService.error).toHaveBeenCalledWith(mockUpdateContactData.resourceBundle.messages.fmsg.m0004);
+    });
+
+    it('should fetch tnc configuration and throw error', () => {
+        jest.spyOn(tncService, 'getTncConfig').mockReturnValue(observableThrowError(mockUpdateContactData.tncConfigIncorrectData) as any);
+        jest.spyOn(toasterService, 'error');
+        component.fetchTncConfiguration();
+        expect(toasterService.error).toHaveBeenCalledWith(mockUpdateContactData.resourceBundle.messages.fmsg.m0004);
+    });
+
+    it('should show tnc popup if given mode is true', () => {
+        component.showAndHidePopup(true);
+        expect(component.showTncPopup).toBe(true);
+    });
+
+    it('should not show tnc popup if given mode is false', () => {
+        component.showAndHidePopup(false);
+        expect(component.showTncPopup).toBe(false);
+    });
+
+    it('should submit form when captcha enabled', () => {
+        component.isP1CaptchaEnabled = 'true';
+        jest.spyOn(component, 'resetGoogleCaptcha');
+        jest.spyOn(component, 'resolved').mockImplementation(() => true);
+        component.submitForm();
+        expect(component.resetGoogleCaptcha).toHaveBeenCalled();
+    });
+
+    it('should submit form when captcha is not enabled', () => {
+        component.isP1CaptchaEnabled = 'false';
+        jest.spyOn(component, 'onFormUpdate');
+        component.submitForm();
+        expect(component.onFormUpdate).toHaveBeenCalled();
+    });
+
+    it('should receive response from captcha', () => {
+        component.isP1CaptchaEnabled = 'true';
+        jest.spyOn(component, 'onFormUpdate').mockImplementation(() => true);
+        component.resolved(mockUpdateContactData.captchaToken);
+        expect(component.onFormUpdate).toHaveBeenCalledWith(mockUpdateContactData.captchaToken);
+    });
+
+    it("should call ngAfterViewInit", (done) => {
+        jest.spyOn(component, 'handleFormChangeEvent').mockImplementation();
+        component.ngAfterViewInit();
+        setTimeout(() => {
+            expect(component.handleFormChangeEvent).toHaveBeenCalled();
+            done()
+        });
+    });
+
+    it("should call handleFormChangeEvent", () => {
+        // @ts-ignore
+        component.contactDetailsForm = new FormGroup({
+            value: new FormControl('12345'),
+            type: new FormControl('phone'),
+            tncAccepted: new FormControl(true),
+            status: new FormControl('VALID'),
+        })
+        component.handleFormChangeEvent();
+        // @ts-ignore
+        component.contactDetailsForm.valueChanges.subscribe((data, data2) => {
+            expect(component.disableSubmitBtn).toBeFalsy();
+        })
+    })
+});
\ No newline at end of file