From d534cda0543f681358ab60bb8cdc9376851b1b06 Mon Sep 17 00:00:00 2001 From: Roman Chygryn <129765378+rmch91@users.noreply.github.com> Date: Thu, 20 Jul 2023 12:52:47 +0200 Subject: [PATCH] CXSPA-4049 Add tests for payment verification process (#17662) --- ...opf-payment-verification.component.spec.ts | 154 ++++++- .../opf-payment-verification.service.spec.ts | 404 ++++++++++++++++++ ...opf-payment-metadata-store.service.spec.ts | 85 ++++ .../opf-state-persistence.service.spec.ts | 108 ++++- .../base/root/services/opf.service.spec.ts | 82 +++- 5 files changed, 830 insertions(+), 3 deletions(-) create mode 100644 integration-libs/opf/base/root/components/opf-payment-verification/opf-payment-verification.service.spec.ts create mode 100644 integration-libs/opf/base/root/services/opf-payment-metadata-store.service.spec.ts diff --git a/integration-libs/opf/base/root/components/opf-payment-verification/opf-payment-verification.component.spec.ts b/integration-libs/opf/base/root/components/opf-payment-verification/opf-payment-verification.component.spec.ts index fdd5fffbb17..5b0c74e9de3 100644 --- a/integration-libs/opf/base/root/components/opf-payment-verification/opf-payment-verification.component.spec.ts +++ b/integration-libs/opf/base/root/components/opf-payment-verification/opf-payment-verification.component.spec.ts @@ -1 +1,153 @@ -// TODO: Add unit tests +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Component } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ActivatedRoute } from '@angular/router'; +import { HttpErrorModel } from '@spartacus/core'; +import { Order } from '@spartacus/order/root'; +import { of, Subscription, throwError } from 'rxjs'; +import { OpfResponseMapElement } from '../../model'; +import { OpfPaymentVerificationComponent } from './opf-payment-verification.component'; +import { OpfPaymentVerificationService } from './opf-payment-verification.service'; + +@Component({ + selector: 'cx-spinner', + template: '', +}) +class MockSpinnerComponent {} + +describe('OpfPaymentVerificationComponent', () => { + let component: OpfPaymentVerificationComponent; + let fixture: ComponentFixture; + let routeMock: jasmine.SpyObj; + let paymentServiceMock: jasmine.SpyObj; + + beforeEach(() => { + routeMock = jasmine.createSpyObj('ActivatedRoute', [], { + snapshot: { queryParamMap: new Map() }, + }); + paymentServiceMock = jasmine.createSpyObj('OpfPaymentVerificationService', [ + 'checkIfProcessingCartIdExist', + 'verifyResultUrl', + 'verifyPayment', + 'placeOrder', + 'goToPage', + 'displayError', + ]); + + TestBed.configureTestingModule({ + declarations: [OpfPaymentVerificationComponent, MockSpinnerComponent], + providers: [ + { provide: ActivatedRoute, useValue: routeMock }, + { + provide: OpfPaymentVerificationService, + useValue: paymentServiceMock, + }, + ], + }); + + fixture = TestBed.createComponent(OpfPaymentVerificationComponent); + component = fixture.componentInstance; + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('ngOnInit', () => { + it('should call checkIfProcessingCartIdExist', () => { + paymentServiceMock.verifyResultUrl.and.returnValue(of()); + + component.ngOnInit(); + expect( + paymentServiceMock.checkIfProcessingCartIdExist + ).toHaveBeenCalled(); + }); + + it('should handle success scenario', () => { + const mockPaymentSessionId = 'sessionId'; + const mockResponseMap: OpfResponseMapElement[] = []; + const mockVerifyResult: { + paymentSessionId: string; + responseMap: OpfResponseMapElement[]; + } = { + paymentSessionId: mockPaymentSessionId, + responseMap: mockResponseMap, + }; + const mockPlaceOrderResult: Order = { guid: 'placeOrderResult' }; + + paymentServiceMock.verifyResultUrl.and.returnValue(of(mockVerifyResult)); + paymentServiceMock.verifyPayment.and.returnValue(of(true)); + paymentServiceMock.placeOrder.and.returnValue(of(mockPlaceOrderResult)); + + component.ngOnInit(); + + expect(paymentServiceMock.verifyResultUrl).toHaveBeenCalledWith( + routeMock + ); + expect(paymentServiceMock.verifyPayment).toHaveBeenCalledWith( + mockPaymentSessionId, + mockResponseMap + ); + expect(paymentServiceMock.placeOrder).toHaveBeenCalled(); + }); + + it('should handle error scenario', () => { + const mockError: HttpErrorModel = { status: 500, message: 'Error' }; + + const mockVerifyResult = { + paymentSessionId: '1', + responseMap: [], + }; + + paymentServiceMock.verifyResultUrl.and.returnValue(of(mockVerifyResult)); + paymentServiceMock.verifyPayment.and.returnValue(throwError(mockError)); + + spyOn(component, 'onError'); + + component.ngOnInit(); + + expect(component.onError).toHaveBeenCalledWith(mockError); + }); + }); + + describe('onSuccess', () => { + it('should call paymentService.goToPage with "orderConfirmation"', () => { + component.onSuccess(); + expect(paymentServiceMock.goToPage).toHaveBeenCalledWith( + 'orderConfirmation' + ); + }); + }); + + describe('onError', () => { + it('should call paymentService.displayError with the provided error and paymentService.goToPage with "checkoutReviewOrder"', () => { + const mockError: HttpErrorModel = { status: 404, message: 'Not Found' }; + + component.onError(mockError); + + expect(paymentServiceMock.displayError).toHaveBeenCalledWith(mockError); + expect(paymentServiceMock.goToPage).toHaveBeenCalledWith( + 'checkoutReviewOrder' + ); + }); + }); + + describe('ngOnDestroy', () => { + it('should unsubscribe from the subscription', () => { + const subscriptionMock: Subscription = jasmine.createSpyObj( + 'Subscription', + ['unsubscribe'] + ); + component.subscription = subscriptionMock; + + component.ngOnDestroy(); + + expect(subscriptionMock.unsubscribe).toHaveBeenCalled(); + }); + }); +}); diff --git a/integration-libs/opf/base/root/components/opf-payment-verification/opf-payment-verification.service.spec.ts b/integration-libs/opf/base/root/components/opf-payment-verification/opf-payment-verification.service.spec.ts new file mode 100644 index 00000000000..16de8a0d9e0 --- /dev/null +++ b/integration-libs/opf/base/root/components/opf-payment-verification/opf-payment-verification.service.spec.ts @@ -0,0 +1,404 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { TestBed } from '@angular/core/testing'; +import { ActivatedRoute, Params } from '@angular/router'; +import { + GlobalMessageService, + GlobalMessageType, + HttpErrorModel, + RoutingService, +} from '@spartacus/core'; +import { Order } from '@spartacus/order/root'; +import { of } from 'rxjs'; +import { OpfOrderFacade, OpfPaymentFacade } from '../../facade'; +import { + OpfPaymentMetadata, + OpfPaymentVerificationResponse, + OpfPaymentVerificationResult, +} from '../../model'; +import { OpfService } from '../../services'; +import { OpfPaymentVerificationService } from './opf-payment-verification.service'; + +describe('OpfPaymentVerificationService', () => { + let service: OpfPaymentVerificationService; + let opfOrderFacadeMock: jasmine.SpyObj; + let routingServiceMock: jasmine.SpyObj; + let globalMessageServiceMock: jasmine.SpyObj; + let opfCheckoutServiceMock: jasmine.SpyObj; + let opfServiceMock: jasmine.SpyObj; + + beforeEach(() => { + opfOrderFacadeMock = jasmine.createSpyObj('OpfOrderFacade', [ + 'placeOpfOrder', + ]); + routingServiceMock = jasmine.createSpyObj('RoutingService', ['go']); + globalMessageServiceMock = jasmine.createSpyObj('GlobalMessageService', [ + 'add', + ]); + opfCheckoutServiceMock = jasmine.createSpyObj('OpfPaymentFacade', [ + 'verifyPayment', + ]); + opfServiceMock = jasmine.createSpyObj('OpfService', [ + 'getOpfMetadataState', + ]); + + TestBed.configureTestingModule({ + providers: [ + OpfPaymentVerificationService, + { provide: OpfOrderFacade, useValue: opfOrderFacadeMock }, + { provide: RoutingService, useValue: routingServiceMock }, + { provide: GlobalMessageService, useValue: globalMessageServiceMock }, + { provide: OpfPaymentFacade, useValue: opfCheckoutServiceMock }, + { provide: OpfService, useValue: opfServiceMock }, + ], + }); + + service = TestBed.inject(OpfPaymentVerificationService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + describe('getOpfResponseMap', () => { + it('should return an empty array for undefined params', () => { + const result = service.getOpfResponseMap(undefined as unknown as Params); + + expect(result).toEqual([]); + }); + + it('should return an array of OpfResponseMapElement for provided params', () => { + const params: Params = { key1: 'value1', key2: 'value2' }; + + const result = service.getOpfResponseMap(params); + + expect(result).toEqual([ + { key: 'key1', value: 'value1' }, + { key: 'key2', value: 'value2' }, + ]); + }); + }); + + describe('findInOpfResponseMap', () => { + it('should return the value for the provided key if found in the list', () => { + const list = [ + { key: 'key1', value: 'value1' }, + { key: 'key2', value: 'value2' }, + ]; + + const result = service.findInOpfResponseMap('key1', list); + + expect(result).toEqual('value1'); + }); + + it('should return undefined if the provided key is not found in the list', () => { + const list = [ + { key: 'key1', value: 'value1' }, + { key: 'key2', value: 'value2' }, + ]; + + const result = service.findInOpfResponseMap('key3', list); + + expect(result).toBeUndefined(); + }); + }); + + describe('goToPage', () => { + it('should call routingService.go with the provided cxRoute', () => { + service.goToPage('orderConfirmation'); + + expect(routingServiceMock.go).toHaveBeenCalledWith({ + cxRoute: 'orderConfirmation', + }); + }); + }); + + describe('verifyResultUrl', () => { + const mockPaymentSessionId = 'sessionId'; + const mockRouteSnapshot: ActivatedRoute = { + routeConfig: { + data: { + cxRoute: 'paymentVerificationResult', + }, + }, + queryParams: of({ paymentSessionId: mockPaymentSessionId }), + } as unknown as ActivatedRoute; + + it('should verify the result URL and return the response map if the route cxRoute is "paymentVerificationResult"', (done) => { + service.verifyResultUrl(mockRouteSnapshot).subscribe((result) => { + expect(result.paymentSessionId).toEqual(mockPaymentSessionId); + expect(result.responseMap).toEqual([ + { key: 'paymentSessionId', value: mockPaymentSessionId }, + ]); + done(); + }); + }); + + it('should throw an error if the route cxRoute is not "paymentVerificationResult"', (done) => { + const mockOtherRouteSnapshot: ActivatedRoute = { + routeConfig: { + data: { cxRoute: 'otherRoute' }, + }, + queryParams: of(), + } as unknown as ActivatedRoute; + + service.verifyResultUrl(mockOtherRouteSnapshot).subscribe( + () => {}, + (error) => { + expect(error.message).toEqual('opf.payment.errors.cancelPayment'); + done(); + } + ); + }); + + it('should throw an error if queryParams is undefined', (done) => { + const mockRoute: ActivatedRoute = { + routeConfig: { + data: { + cxRoute: 'paymentVerificationResult', + }, + }, + queryParams: of({}), + } as unknown as ActivatedRoute; + + service.verifyResultUrl(mockRoute).subscribe( + () => {}, + (error) => { + expect(error.message).toEqual('opf.payment.errors.proceedPayment'); + done(); + } + ); + }); + }); + + describe('placeOrder', () => { + it('should call opfOrderFacade.placeOpfOrder with true and return the result', (done) => { + const mockPlaceOrderResult: Order = { guid: 'placeOrderResult' }; + opfOrderFacadeMock.placeOpfOrder.and.returnValue( + of(mockPlaceOrderResult) + ); + + service.placeOrder().subscribe((result) => { + expect(result).toEqual(mockPlaceOrderResult); + expect(opfOrderFacadeMock.placeOpfOrder).toHaveBeenCalledWith(true); + done(); + }); + }); + }); + + describe('verifyPayment', () => { + it('should call opfCheckoutService.verifyPayment and return true if the result is AUTHORIZED', (done) => { + const mockPaymentSessionId = 'sessionId'; + const mockResponseMap = [{ key: 'key', value: 'value' }]; + const mockVerificationResponse: OpfPaymentVerificationResponse = { + result: OpfPaymentVerificationResult.AUTHORIZED, + }; + + opfCheckoutServiceMock.verifyPayment.and.returnValue( + of(mockVerificationResponse) + ); + + service + .verifyPayment(mockPaymentSessionId, mockResponseMap) + .subscribe((result) => { + expect(result).toBeTruthy(); + expect(opfCheckoutServiceMock.verifyPayment).toHaveBeenCalledWith( + mockPaymentSessionId, + { responseMap: mockResponseMap } + ); + done(); + }); + }); + + it('should call opfCheckoutService.verifyPayment and return true if the result is DELAYED', (done) => { + const mockPaymentSessionId = 'sessionId'; + const mockResponseMap = [{ key: 'key', value: 'value' }]; + const mockVerificationResponse: OpfPaymentVerificationResponse = { + result: OpfPaymentVerificationResult.DELAYED, + }; + + opfCheckoutServiceMock.verifyPayment.and.returnValue( + of(mockVerificationResponse) + ); + + service + .verifyPayment(mockPaymentSessionId, mockResponseMap) + .subscribe((result) => { + expect(result).toBeTruthy(); + expect(opfCheckoutServiceMock.verifyPayment).toHaveBeenCalledWith( + mockPaymentSessionId, + { responseMap: mockResponseMap } + ); + done(); + }); + }); + + it('should throw an error with "opf.payment.errors.cancelPayment" if the result is CANCELLED', (done) => { + const mockPaymentSessionId = 'sessionId'; + const mockResponseMap = [{ key: 'key', value: 'value' }]; + const mockVerificationResponse: OpfPaymentVerificationResponse = { + result: OpfPaymentVerificationResult.CANCELLED, + }; + + opfCheckoutServiceMock.verifyPayment.and.returnValue( + of(mockVerificationResponse) + ); + + service.verifyPayment(mockPaymentSessionId, mockResponseMap).subscribe( + () => {}, + (error) => { + expect(error.message).toEqual('opf.payment.errors.cancelPayment'); + done(); + } + ); + }); + + it('should throw an error with defaultError if the result is not AUTHORIZED, DELAYED, or CANCELLED', (done) => { + const mockPaymentSessionId = 'sessionId'; + const mockResponseMap = [{ key: 'key', value: 'value' }]; + const mockVerificationResponse: OpfPaymentVerificationResponse = { + result: 'ERROR', + }; + + opfCheckoutServiceMock.verifyPayment.and.returnValue( + of(mockVerificationResponse) + ); + + service.verifyPayment(mockPaymentSessionId, mockResponseMap).subscribe( + () => {}, + (error) => { + expect(error).toEqual(service.defaultError); + done(); + } + ); + }); + }); + + describe('isPaymentSuccessful', () => { + it('should return true if the response result is AUTHORIZED', (done) => { + const mockVerificationResponse: OpfPaymentVerificationResponse = { + result: OpfPaymentVerificationResult.AUTHORIZED, + }; + + service + .isPaymentSuccessful(mockVerificationResponse) + .subscribe((result) => { + expect(result).toBeTruthy(); + done(); + }); + }); + + it('should return true if the response result is DELAYED', (done) => { + const mockVerificationResponse: OpfPaymentVerificationResponse = { + result: OpfPaymentVerificationResult.DELAYED, + }; + + service + .isPaymentSuccessful(mockVerificationResponse) + .subscribe((result) => { + expect(result).toBeTruthy(); + done(); + }); + }); + + it('should throw an error with "opf.payment.errors.cancelPayment" if the response result is CANCELLED', (done) => { + const mockVerificationResponse: OpfPaymentVerificationResponse = { + result: OpfPaymentVerificationResult.CANCELLED, + }; + + service.isPaymentSuccessful(mockVerificationResponse).subscribe( + () => {}, + (error) => { + expect(error.message).toEqual('opf.payment.errors.cancelPayment'); + done(); + } + ); + }); + + it('should throw an error with defaultError if the response result is not AUTHORIZED, DELAYED, or CANCELLED', (done) => { + const mockVerificationResponse: OpfPaymentVerificationResponse = { + result: 'ERROR', + }; + + service.isPaymentSuccessful(mockVerificationResponse).subscribe( + () => {}, + (error) => { + expect(error).toEqual(service.defaultError); + done(); + } + ); + }); + }); + + describe('displayError', () => { + it('should display the provided error message as an error global message', () => { + const mockError: HttpErrorModel = { status: -1, message: 'Custom Error' }; + + service.displayError(mockError); + + expect(globalMessageServiceMock.add).toHaveBeenCalledWith( + { key: mockError.message }, + GlobalMessageType.MSG_TYPE_ERROR + ); + }); + + it('should display default error message as an error global message when the provided error does not have status -1', () => { + const mockError: HttpErrorModel = { + status: 500, + message: 'Internal Server Error', + }; + + service.displayError(mockError); + + expect(globalMessageServiceMock.add).toHaveBeenCalledWith( + { key: 'opf.payment.errors.proceedPayment' }, + GlobalMessageType.MSG_TYPE_ERROR + ); + }); + }); + + describe('checkIfProcessingCartIdExist', () => { + it('should not do anything if the opfPaymentMetadata isPaymentInProgress is true', () => { + const mockOpfPaymentMetadata: OpfPaymentMetadata = { + isPaymentInProgress: true, + selectedPaymentOptionId: 111, + termsAndConditionsChecked: true, + }; + + opfServiceMock.getOpfMetadataState.and.returnValue( + of(mockOpfPaymentMetadata) + ); + + service.checkIfProcessingCartIdExist(); + + expect(opfServiceMock.getOpfMetadataState).toHaveBeenCalled(); + expect(globalMessageServiceMock.add).not.toHaveBeenCalled(); + expect(routingServiceMock.go).not.toHaveBeenCalled(); + }); + + it('should go to "cart" page and add global error message if the opfPaymentMetadata isPaymentInProgress is false', () => { + const mockOpfPaymentMetadata: OpfPaymentMetadata = { + isPaymentInProgress: false, + selectedPaymentOptionId: 111, + termsAndConditionsChecked: true, + }; + + opfServiceMock.getOpfMetadataState.and.returnValue( + of(mockOpfPaymentMetadata) + ); + + service.checkIfProcessingCartIdExist(); + + expect(opfServiceMock.getOpfMetadataState).toHaveBeenCalled(); + expect(globalMessageServiceMock.add).toHaveBeenCalledWith( + { key: 'httpHandlers.cartNotFound' }, + GlobalMessageType.MSG_TYPE_ERROR + ); + expect(routingServiceMock.go).toHaveBeenCalledWith({ cxRoute: 'cart' }); + }); + }); +}); diff --git a/integration-libs/opf/base/root/services/opf-payment-metadata-store.service.spec.ts b/integration-libs/opf/base/root/services/opf-payment-metadata-store.service.spec.ts new file mode 100644 index 00000000000..60900606521 --- /dev/null +++ b/integration-libs/opf/base/root/services/opf-payment-metadata-store.service.spec.ts @@ -0,0 +1,85 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { TestBed } from '@angular/core/testing'; +import { OpfPaymentMetadata } from '../model'; +import { OpfPaymentMetadataStoreService } from './opf-payment-metadata-store.service'; + +const initialState = { + termsAndConditionsChecked: false, + selectedPaymentOptionId: undefined, + isPaymentInProgress: false, +}; + +const state: OpfPaymentMetadata = { + isPaymentInProgress: true, + selectedPaymentOptionId: 111, + termsAndConditionsChecked: true, +}; + +describe('OpfPaymentMetadataStoreService', () => { + let service: OpfPaymentMetadataStoreService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [OpfPaymentMetadataStoreService], + }); + + service = TestBed.inject(OpfPaymentMetadataStoreService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should initialize with the initial state', () => { + expect(service.opfPaymentMetadataState.value).toEqual(initialState); + }); + + it('should return the current opfPaymentMetadataState as an observable', (done) => { + service.opfPaymentMetadataState.next(state); + + service.getOpfMetadataState().subscribe((state) => { + expect(state).toEqual(state); + done(); + }); + }); + + it('should update opfPaymentMetadataState with the given payload', () => { + const mockedState: OpfPaymentMetadata = { + ...state, + isPaymentInProgress: false, + }; + + service.opfPaymentMetadataState.next(mockedState); + + const updatedPayload = { + isPaymentInProgress: true, + termsAndConditionsChecked: false, + }; + + service.updateOpfMetadata(updatedPayload); + + expect(service.opfPaymentMetadataState.value).toEqual({ + ...mockedState, + ...updatedPayload, + }); + }); + + it('should clear opfPaymentMetadataState and set it back to the initial state', () => { + const state = { + isPaymentInProgress: true, + termsAndConditionsChecked: true, + selectedPaymentOptionId: 111, + }; + + service.opfPaymentMetadataState.next(state); + + service.clearOpfMetadata(); + + expect(service.opfPaymentMetadataState.value).toEqual(initialState); + }); +}); diff --git a/integration-libs/opf/base/root/services/opf-state-persistence.service.spec.ts b/integration-libs/opf/base/root/services/opf-state-persistence.service.spec.ts index a802fe01212..70cd2e7d15a 100644 --- a/integration-libs/opf/base/root/services/opf-state-persistence.service.spec.ts +++ b/integration-libs/opf/base/root/services/opf-state-persistence.service.spec.ts @@ -1 +1,107 @@ -// TODO: Add unit tests... +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { TestBed } from '@angular/core/testing'; +import { StatePersistenceService } from '@spartacus/core'; +import { BehaviorSubject, of, Subscription } from 'rxjs'; +import { OpfPaymentMetadata } from '../model'; +import { OpfPaymentMetadataStoreService } from './opf-payment-metadata-store.service'; +import { + OpfStatePersistenceService, + SyncedOpfState, +} from './opf-state-persistence.service'; + +const mockOpfMetadata: OpfPaymentMetadata = { + isPaymentInProgress: true, + selectedPaymentOptionId: 111, + termsAndConditionsChecked: true, +}; + +describe('OpfStatePersistenceService', () => { + let service: OpfStatePersistenceService; + let statePersistenceServiceMock: jasmine.SpyObj; + let opfPaymentMetadataStoreServiceMock: jasmine.SpyObj; + + beforeEach(() => { + statePersistenceServiceMock = jasmine.createSpyObj( + 'StatePersistenceService', + ['syncWithStorage'] + ); + opfPaymentMetadataStoreServiceMock = jasmine.createSpyObj( + 'OpfPaymentMetadataStoreService', + ['getOpfMetadataState', 'updateOpfMetadata'] + ); + + TestBed.configureTestingModule({ + providers: [ + OpfStatePersistenceService, + { + provide: StatePersistenceService, + useValue: statePersistenceServiceMock, + }, + { + provide: OpfPaymentMetadataStoreService, + useValue: opfPaymentMetadataStoreServiceMock, + }, + ], + }); + + service = TestBed.inject(OpfStatePersistenceService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should initialize the synchronization with state and browser storage', () => { + const mockSyncedOpfState: SyncedOpfState = { + metadata: mockOpfMetadata, + }; + + const stateObservable = new BehaviorSubject( + mockSyncedOpfState + ); + opfPaymentMetadataStoreServiceMock.getOpfMetadataState.and.returnValue( + of(stateObservable.value?.metadata) + ); + + service.initSync(); + + expect(statePersistenceServiceMock.syncWithStorage).toHaveBeenCalled(); + }); + + it('should get and transform Opf state', (done) => { + const stateObservable = new BehaviorSubject( + mockOpfMetadata + ); + opfPaymentMetadataStoreServiceMock.getOpfMetadataState.and.returnValue( + stateObservable + ); + + service['getOpfState']().subscribe((state) => { + expect(state).toEqual({ metadata: mockOpfMetadata }); + done(); + }); + }); + + it('should update OpfPaymentMetadataStoreService when onRead is called', () => { + const mockSyncedOpfState: SyncedOpfState = { + metadata: mockOpfMetadata, + }; + + service['onRead'](mockSyncedOpfState); + + expect( + opfPaymentMetadataStoreServiceMock.updateOpfMetadata + ).toHaveBeenCalledWith(mockOpfMetadata); + }); + + it('should unsubscribe on ngOnDestroy', () => { + spyOn(Subscription.prototype, 'unsubscribe'); + service.ngOnDestroy(); + expect(Subscription.prototype.unsubscribe).toHaveBeenCalled(); + }); +}); diff --git a/integration-libs/opf/base/root/services/opf.service.spec.ts b/integration-libs/opf/base/root/services/opf.service.spec.ts index c233f2d9ee2..f8bd33f2850 100644 --- a/integration-libs/opf/base/root/services/opf.service.spec.ts +++ b/integration-libs/opf/base/root/services/opf.service.spec.ts @@ -4,4 +4,84 @@ * SPDX-License-Identifier: Apache-2.0 */ -// TODO: Add unit tests +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { TestBed } from '@angular/core/testing'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { OpfPaymentMetadata } from '../model'; +import { OpfPaymentMetadataStoreService } from './opf-payment-metadata-store.service'; +import { OpfService } from './opf.service'; + +describe('OpfService', () => { + let service: OpfService; + let opfPaymentMetadataStoreServiceMock: jasmine.SpyObj; + + beforeEach(() => { + opfPaymentMetadataStoreServiceMock = jasmine.createSpyObj( + 'OpfPaymentMetadataStoreService', + ['updateOpfMetadata', 'clearOpfMetadata', 'getOpfMetadataState'] + ); + + TestBed.configureTestingModule({ + providers: [ + OpfService, + { + provide: OpfPaymentMetadataStoreService, + useValue: opfPaymentMetadataStoreServiceMock, + }, + ], + }); + + service = TestBed.inject(OpfService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should call updateOpfMetadataState with the provided payload', () => { + const mockOpfMetadata: Partial = { + isPaymentInProgress: true, + }; + + service.updateOpfMetadataState(mockOpfMetadata); + + expect( + opfPaymentMetadataStoreServiceMock.updateOpfMetadata + ).toHaveBeenCalledWith(mockOpfMetadata); + }); + + it('should call clearOpfMetadataState', () => { + service.clearOpfMetadataState(); + + expect( + opfPaymentMetadataStoreServiceMock.clearOpfMetadata + ).toHaveBeenCalled(); + }); + + it('should call getOpfMetadataState and return the observable', () => { + const mockOpfMetadata: OpfPaymentMetadata = { + isPaymentInProgress: true, + selectedPaymentOptionId: 111, + termsAndConditionsChecked: true, + }; + + const mockObservable = new BehaviorSubject( + mockOpfMetadata + ); + opfPaymentMetadataStoreServiceMock.getOpfMetadataState.and.returnValue( + mockObservable + ); + + const result = service.getOpfMetadataState(); + + expect(result).toBeInstanceOf(Observable); + result.subscribe((metadata) => { + expect(metadata).toEqual(mockOpfMetadata); + }); + }); +});