diff --git a/feature-libs/cart/base/core/facade/cart-access-code.service.spec.ts b/feature-libs/cart/base/core/facade/cart-access-code.service.spec.ts index fdd5fffbb17..0bed474c0db 100644 --- a/feature-libs/cart/base/core/facade/cart-access-code.service.spec.ts +++ b/feature-libs/cart/base/core/facade/cart-access-code.service.spec.ts @@ -1 +1,65 @@ -// TODO: Add unit tests +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { TestBed } from '@angular/core/testing'; +import { CartAccessCodeService } from './cart-access-code.service'; +import { CommandService, QueryService } from '@spartacus/core'; +import { CartAccessCodeConnector } from '../connectors'; +import { of } from 'rxjs'; +import createSpy = jasmine.createSpy; + +describe('CartAccessCodeService', () => { + let service: CartAccessCodeService; + let commandService: CommandService; + let cartAccessCodeConnector: CartAccessCodeConnector; + + beforeEach(() => { + const commandServiceMock = { + create: createSpy().and.callFake((fn: any) => ({ + execute: fn, + })), + }; + + const cartAccessCodeConnectorMock = { + getCartAccessCode: createSpy().and.returnValue(of('mockAccessCode')), + }; + + TestBed.configureTestingModule({ + providers: [ + CartAccessCodeService, + { provide: CommandService, useValue: commandServiceMock }, + { + provide: CartAccessCodeConnector, + useValue: cartAccessCodeConnectorMock, + }, + QueryService, + ], + }); + + service = TestBed.inject(CartAccessCodeService); + commandService = TestBed.inject(CommandService); + cartAccessCodeConnector = TestBed.inject(CartAccessCodeConnector); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should call CartAccessCodeConnector.getCartAccessCode when getCartAccessCode is executed', () => { + const userId = 'testUserId'; + const cartId = 'testCartId'; + + service.getCartAccessCode(userId, cartId).subscribe((result) => { + expect(result).toBe('mockAccessCode'); + }); + + expect(cartAccessCodeConnector.getCartAccessCode).toHaveBeenCalledWith( + userId, + cartId + ); + expect(commandService.create).toHaveBeenCalled(); + }); +}); diff --git a/integration-libs/opf/base/core/facade/opf-base.service.spec.ts b/integration-libs/opf/base/core/facade/opf-base.service.spec.ts index d6b6fe4e54c..40d7f3b7284 100644 --- a/integration-libs/opf/base/core/facade/opf-base.service.spec.ts +++ b/integration-libs/opf/base/core/facade/opf-base.service.spec.ts @@ -4,326 +4,112 @@ * SPDX-License-Identifier: Apache-2.0 */ -// TODO: Add unit tests - -// import { TestBed } from '@angular/core/testing'; -// import { CommandService, QueryService } from '@spartacus/core'; -// import { Observable, of } from 'rxjs'; -// import { -// ActiveConfiguration, -// AfterRedirectScriptResponse, -// CtaScriptsLocation, -// CtaScriptsRequest, -// CtaScriptsResponse, -// OpfPaymentProviderType, -// OpfPaymentVerificationPayload, -// OpfPaymentVerificationResponse, -// SubmitCompleteInput, -// SubmitInput, -// } from '../../root/model'; -// import { OpfPaymentConnector } from '../connectors'; -// import { OpfPaymentHostedFieldsService } from '../services'; -// import { OpfPaymentService } from './opf-base.service'; - -// class MockPaymentConnector implements Partial { -// verifyPayment( -// _paymentSessionId: string, -// _payload: OpfPaymentVerificationPayload -// ): Observable { -// return of({ -// result: 'result', -// }) as Observable; -// } -// afterRedirectScripts( -// _paymentSessionId: string -// ): Observable { -// return of({ afterRedirectScript: {} }); -// } -// getActiveConfigurations(): Observable { -// return of(mockActiveConfigurations); -// } -// getCtaScripts( -// _ctaScriptsRequest: CtaScriptsRequest -// ): Observable { -// return of(MockCtaScriptsResponse); -// } -// } - -// class MockOpfPaymentHostedFieldsService { -// submitPayment(): Observable { -// return of(true); -// } - -// submitCompletePayment(): Observable { -// return of(true); -// } -// } - -// const mockSubmitInput = { -// cartId: '123', -// } as SubmitInput; - -// const mockActiveConfigurations: ActiveConfiguration[] = [ -// { -// id: 1, -// providerType: OpfPaymentProviderType.PAYMENT_GATEWAY, -// displayName: 'Test1', -// }, -// { -// id: 2, -// providerType: OpfPaymentProviderType.PAYMENT_METHOD, -// displayName: 'Test2', -// }, -// ]; - -// const MockCtaRequest: CtaScriptsRequest = { -// cartId: '00259012', -// ctaProductItems: [ -// { -// productId: '301233', -// quantity: 1, -// }, -// { -// productId: '2231913', -// quantity: 1, -// }, -// { -// productId: '1776948', -// quantity: 1, -// }, -// ], -// scriptLocations: [CtaScriptsLocation.ORDER_HISTORY_PAYMENT_GUIDE], -// }; - -// const MockCtaScriptsResponse: CtaScriptsResponse = { -// value: [ -// { -// paymentAccountId: 1, -// dynamicScript: { -// html: "

CTA Html snippet #1

", -// cssUrls: [ -// { -// url: 'https://checkoutshopper-test.adyen.com/checkoutshopper/sdk/4.2.1/adyen.css', -// sri: '', -// }, -// ], -// jsUrls: [ -// { -// url: 'https://checkoutshopper-test.adyen.com/checkoutshopper/sdk/4.2.1/adyen.js', -// sri: '', -// }, -// ], -// }, -// }, -// ], -// }; - -// describe('OpfPaymentService', () => { -// let service: OpfPaymentService; -// let paymentConnector: MockPaymentConnector; -// let opfPaymentHostedFieldsServiceSpy: OpfPaymentHostedFieldsService; - -// beforeEach(() => { -// TestBed.configureTestingModule({ -// providers: [ -// OpfPaymentService, -// QueryService, -// CommandService, -// { -// provide: OpfPaymentConnector, -// useClass: MockPaymentConnector, -// }, -// { -// provide: OpfPaymentHostedFieldsService, -// useClass: MockOpfPaymentHostedFieldsService, -// }, -// ], -// }); - -// service = TestBed.inject(OpfPaymentService); -// paymentConnector = TestBed.inject(OpfPaymentConnector); -// opfPaymentHostedFieldsServiceSpy = TestBed.inject( -// OpfPaymentHostedFieldsService -// ); -// }); - -// it('should be created', () => { -// expect(service).toBeTruthy(); -// }); - -// it('should call submitPayment with proper payload after submitPaymentCommand execution', () => { -// const submitPaymentSpy = spyOn( -// opfPaymentHostedFieldsServiceSpy, -// 'submitPayment' -// ).and.callThrough(); - -// const submitInput: SubmitInput = { -// cartId: 'testCart', -// } as SubmitInput; - -// service['submitPaymentCommand'].execute({ submitInput }); - -// expect(submitPaymentSpy).toHaveBeenCalledWith(submitInput); -// }); - -// it('should call verifyPayment with proper payload after verifyPaymentCommand execution', () => { -// const verifyPaymentSpy = spyOn( -// paymentConnector, -// 'verifyPayment' -// ).and.callThrough(); - -// const verifyPaymentPayload = { -// paymentSessionId: 'exampleSessionId', -// paymentVerificationPayload: { -// responseMap: [], -// }, -// }; - -// service['verifyPaymentCommand'].execute(verifyPaymentPayload); - -// expect(verifyPaymentSpy).toHaveBeenCalledWith( -// verifyPaymentPayload.paymentSessionId, -// verifyPaymentPayload.paymentVerificationPayload -// ); -// }); - -// it('should call submitCompletePayment with proper payload after submitCompletePaymentCommand execution', () => { -// const submitCompletePaymentSpy = spyOn( -// opfPaymentHostedFieldsServiceSpy, -// 'submitCompletePayment' -// ).and.callThrough(); - -// const submitCompleteInput: SubmitCompleteInput = { -// cartId: 'testCart', -// } as SubmitCompleteInput; - -// service['submitCompletePaymentCommand'].execute({ submitCompleteInput }); - -// expect(submitCompletePaymentSpy).toHaveBeenCalledWith(submitCompleteInput); -// }); - -// it('should call verifyPayment from connector with the correct payload', () => { -// const paymentSessionId = 'exampleSessionId'; -// const paymentVerificationPayload = { -// responseMap: [ -// { -// key: 'key', -// value: 'value', -// }, -// ], -// } as OpfPaymentVerificationPayload; -// const connectorVerifySpy = spyOn( -// paymentConnector, -// 'verifyPayment' -// ).and.callThrough(); - -// service.verifyPayment(paymentSessionId, paymentVerificationPayload); - -// expect(connectorVerifySpy).toHaveBeenCalledWith( -// paymentSessionId, -// paymentVerificationPayload -// ); -// }); - -// it('should call submitPayment from opfPaymentHostedFieldsService with the correct payload', () => { -// const submitPaymentSpy = spyOn( -// opfPaymentHostedFieldsServiceSpy, -// 'submitPayment' -// ).and.callThrough(); - -// service.submitPayment(mockSubmitInput); - -// expect(submitPaymentSpy).toHaveBeenCalledWith(mockSubmitInput); -// }); - -// it('should call submitCompletePayment from opfPaymentHostedFieldsService with the correct payload', () => { -// const submitCompletePaymentSpy = spyOn( -// opfPaymentHostedFieldsServiceSpy, -// 'submitCompletePayment' -// ).and.callThrough(); - -// service.submitCompletePayment(mockSubmitInput); - -// expect(submitCompletePaymentSpy).toHaveBeenCalledWith(mockSubmitInput); -// }); - -// it('should return true when payment submission is successful', (done) => { -// const result = service.submitPayment(mockSubmitInput); - -// result.subscribe((response) => { -// expect(response).toBe(true); -// done(); -// }); -// }); - -// it('should return a successful payment verification response', (done) => { -// const paymentSessionId = 'exampleSessionId'; -// const paymentVerificationPayload = { -// responseMap: [ -// { -// key: 'key', -// value: 'value', -// }, -// ], -// } as OpfPaymentVerificationPayload; - -// const expectedResult = { -// result: 'result', -// } as OpfPaymentVerificationResponse; - -// const result = service.verifyPayment( -// paymentSessionId, -// paymentVerificationPayload -// ); - -// result.subscribe((response) => { -// expect(response).toEqual(expectedResult); -// done(); -// }); -// }); - -// it('should return true when payment submission is completed successfully', (done) => { -// const result = service.submitCompletePayment(mockSubmitInput); - -// result.subscribe((response) => { -// expect(response).toBe(true); -// done(); -// }); -// }); - -// it('should call afterRedirectScripts from connector with the correct payload', () => { -// const paymentSessionId = 'exampleSessionId'; - -// const connectorSpy = spyOn( -// paymentConnector, -// 'afterRedirectScripts' -// ).and.callThrough(); - -// service.afterRedirectScripts(paymentSessionId); - -// expect(connectorSpy).toHaveBeenCalledWith(paymentSessionId); -// }); - -// it(`should return mockActiveConfigurations data`, (done) => { -// service.getActiveConfigurationsState().subscribe((state) => { -// expect(state).toEqual({ -// loading: false, -// error: false, -// data: mockActiveConfigurations, -// }); -// done(); -// }); -// }); - -// it(`should return ctaScripts data`, (done) => { -// const connectorCtaSpy = spyOn( -// paymentConnector, -// 'getCtaScripts' -// ).and.callThrough(); - -// service.getCtaScripts(MockCtaRequest).subscribe(() => { -// expect(connectorCtaSpy).toHaveBeenCalledWith(MockCtaRequest); -// done(); -// }); -// }); -// }); +import { TestBed } from '@angular/core/testing'; +import { of } from 'rxjs'; +import { QueryService, QueryState } from '@spartacus/core'; +import { OpfBaseConnector } from '../connectors/opf-base.connector'; +import { OpfBaseService } from './opf-base.service'; +import { + ActiveConfiguration, + OpfPaymentProviderType, +} from '@spartacus/opf/base/root'; + +describe('OpfBaseService', () => { + let service: OpfBaseService; + let queryService: jasmine.SpyObj; + let opfBaseConnector: jasmine.SpyObj; + + beforeEach(() => { + const queryServiceSpy = jasmine.createSpyObj('QueryService', ['create']); + const opfBaseConnectorSpy = jasmine.createSpyObj('OpfBaseConnector', [ + 'getActiveConfigurations', + ]); + + TestBed.configureTestingModule({ + providers: [ + OpfBaseService, + { provide: QueryService, useValue: queryServiceSpy }, + { provide: OpfBaseConnector, useValue: opfBaseConnectorSpy }, + ], + }); + + service = TestBed.inject(OpfBaseService); + queryService = TestBed.inject(QueryService) as jasmine.SpyObj; + opfBaseConnector = TestBed.inject( + OpfBaseConnector + ) as jasmine.SpyObj; + + const mockActiveConfigurations: ActiveConfiguration[] = [ + { + id: 1, + description: 'Payment gateway for merchant 123', + merchantId: '123', + providerType: OpfPaymentProviderType.PAYMENT_GATEWAY, + displayName: 'Gateway 123', + acquirerCountryCode: 'US', + }, + { + id: 2, + description: 'Payment method for merchant 456', + merchantId: '456', + providerType: OpfPaymentProviderType.PAYMENT_METHOD, + displayName: 'Method 456', + acquirerCountryCode: 'GB', + }, + ]; + + opfBaseConnector.getActiveConfigurations.and.returnValue( + of(mockActiveConfigurations) + ); + + const mockQuery = { + get: jasmine + .createSpy('get') + .and.returnValue(of(mockActiveConfigurations)), + getState: jasmine.createSpy('getState').and.returnValue( + of({ + loading: false, + error: undefined, + data: mockActiveConfigurations, + }) + ), + }; + + queryService.create.and.returnValue(mockQuery); + service['activeConfigurationsQuery'] = mockQuery; + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('getActiveConfigurationsState should return an observable with the correct state and call the connector', (done: DoneFn) => { + service + .getActiveConfigurationsState() + .subscribe((state: QueryState) => { + expect(state.loading).toBeFalsy(); + expect(state.error).toBeUndefined(); + expect(state.data).toEqual([ + { + id: 1, + description: 'Payment gateway for merchant 123', + merchantId: '123', + providerType: OpfPaymentProviderType.PAYMENT_GATEWAY, + displayName: 'Gateway 123', + acquirerCountryCode: 'US', + }, + { + id: 2, + description: 'Payment method for merchant 456', + merchantId: '456', + providerType: OpfPaymentProviderType.PAYMENT_METHOD, + displayName: 'Method 456', + acquirerCountryCode: 'GB', + }, + ]); + done(); + }); + + expect(queryService.create).toHaveBeenCalled(); + }); +}); diff --git a/integration-libs/opf/base/opf-api/adapters/opf-api-base.adapter.spec.ts b/integration-libs/opf/base/opf-api/adapters/opf-api-base.adapter.spec.ts index 24c31333528..a1f6a8fade6 100644 --- a/integration-libs/opf/base/opf-api/adapters/opf-api-base.adapter.spec.ts +++ b/integration-libs/opf/base/opf-api/adapters/opf-api-base.adapter.spec.ts @@ -4,245 +4,132 @@ * SPDX-License-Identifier: Apache-2.0 */ -// TODO: Add unit tests... - -// import { -// HttpClient, -// HttpErrorResponse, -// HttpHeaders, -// } from '@angular/common/http'; -// import { -// HttpClientTestingModule, -// HttpTestingController, -// } from '@angular/common/http/testing'; -// import { fakeAsync, TestBed, tick } from '@angular/core/testing'; -// import { -// BaseOccUrlProperties, -// ConverterService, -// DynamicAttributes, -// HttpErrorModel, -// normalizeHttpError, -// } from '@spartacus/core'; -// import { defer, of, throwError } from 'rxjs'; -// import { take } from 'rxjs/operators'; -// import { OpfEndpointsService } from '../../core/services'; -// import { OPF_PAYMENT_VERIFICATION_NORMALIZER } from '../../core/tokens'; -// import { OpfConfig } from '../../root/config'; -// import { OpfPaymentVerificationResponse } from '../../root/model'; -// import { OccOpfPaymentAdapter } from './occ-opf.adapter'; - -// const mockJaloError = new HttpErrorResponse({ -// error: { -// errors: [ -// { -// message: 'The application has encountered an error', -// type: 'JaloObjectNoLongerValidError', -// }, -// ], -// }, -// }); - -// const mockOpfConfig: OpfConfig = {}; - -// const mockPayload = { -// responseMap: [ -// { -// key: 'key', -// value: 'value', -// }, -// ], -// }; - -// const mockResult: OpfPaymentVerificationResponse = { -// result: 'mockResult', -// }; - -// export class MockOpfEndpointsService implements Partial { -// buildUrl( -// endpoint: string, -// _attributes?: DynamicAttributes, -// _propertiesToOmit?: BaseOccUrlProperties -// ) { -// return this.getEndpoint(endpoint); -// } -// getEndpoint(endpoint: string) { -// if (!endpoint.startsWith('/')) { -// endpoint = '/' + endpoint; -// } -// return endpoint; -// } -// } - -// const mockPaymentSessionId = '123'; - -// const mockNormalizedJaloError = normalizeHttpError(mockJaloError); - -// const mock500Error = new HttpErrorResponse({ -// error: 'error', -// headers: new HttpHeaders().set('xxx', 'xxx'), -// status: 500, -// statusText: 'Unknown error', -// url: '/xxx', -// }); - -// const mockNormalized500Error = normalizeHttpError(mock500Error); - -// describe(`OccOpfPaymentAdapter`, () => { -// let service: OccOpfPaymentAdapter; -// let httpMock: HttpTestingController; -// let converter: ConverterService; -// let opfEndpointsService: OpfEndpointsService; -// let httpClient: HttpClient; - -// beforeEach(() => { -// TestBed.configureTestingModule({ -// imports: [HttpClientTestingModule], -// providers: [ -// OccOpfPaymentAdapter, -// { -// provide: OpfEndpointsService, -// useClass: MockOpfEndpointsService, -// }, -// { -// provide: OpfConfig, -// useValue: mockOpfConfig, -// }, -// ], -// }); - -// service = TestBed.inject(OccOpfPaymentAdapter); -// httpMock = TestBed.inject(HttpTestingController); -// httpClient = TestBed.inject(HttpClient); -// converter = TestBed.inject(ConverterService); -// opfEndpointsService = TestBed.inject(OpfEndpointsService); -// spyOn(converter, 'convert').and.callThrough(); -// spyOn(converter, 'pipeable').and.callThrough(); -// spyOn(opfEndpointsService, 'buildUrl').and.callThrough(); -// }); - -// afterEach(() => { -// httpMock.verify(); -// }); - -// it('should be created', () => { -// expect(service).toBeTruthy(); -// }); - -// describe(`verifyPayment`, () => { -// it(`should get all supported delivery modes for cart for given user id and cart id`, (done) => { -// service -// .verifyPayment(mockPaymentSessionId, mockPayload) -// .pipe(take(1)) -// .subscribe((result) => { -// expect(result).toEqual(mockResult); -// done(); -// }); - -// const url = service['verifyPaymentEndpoint'](mockPaymentSessionId); -// const mockReq = httpMock.expectOne(url); - -// expect(mockReq.cancelled).toBeFalsy(); -// expect(mockReq.request.responseType).toEqual('json'); -// mockReq.flush(mockResult); -// expect(converter.pipeable).toHaveBeenCalledWith( -// OPF_PAYMENT_VERIFICATION_NORMALIZER -// ); -// }); - -// describe(`back-off`, () => { -// it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { -// spyOn(httpClient, 'post').and.returnValue(throwError(mockJaloError)); - -// let result: HttpErrorModel | undefined; -// const subscription = service -// .verifyPayment(mockPaymentSessionId, mockPayload) -// .subscribe({ error: (err) => (result = err) }); - -// tick(4200); - -// expect(result).toEqual(mockNormalizedJaloError); - -// subscription.unsubscribe(); -// })); - -// it(`should successfully backOff on Jalo error and recover after the third retry`, fakeAsync(() => { -// let calledTimes = -1; - -// spyOn(httpClient, 'post').and.returnValue( -// defer(() => { -// calledTimes++; -// if (calledTimes === 3) { -// return of(mockResult); -// } -// return throwError(mockJaloError); -// }) -// ); - -// let result: OpfPaymentVerificationResponse | undefined; -// const subscription = service -// .verifyPayment(mockPaymentSessionId, mockPayload) -// .pipe(take(1)) -// .subscribe((res) => (result = res)); - -// // 1*1*300 = 300 -// tick(300); -// expect(result).toEqual(undefined); - -// // 2*2*300 = 1200 -// tick(1200); -// expect(result).toEqual(undefined); - -// // 3*3*300 = 2700 -// tick(2700); - -// expect(result).toEqual(mockResult); -// subscription.unsubscribe(); -// })); - -// it(`should successfully backOff on 500 error and recover after the 2nd retry`, fakeAsync(() => { -// let calledTimes = -1; - -// spyOn(httpClient, 'post').and.returnValue( -// defer(() => { -// calledTimes++; -// if (calledTimes === 2) { -// return of(mockResult); -// } -// return throwError(mock500Error); -// }) -// ); - -// let result: OpfPaymentVerificationResponse | undefined; -// const subscription = service -// .verifyPayment(mockPaymentSessionId, mockPayload) -// .pipe(take(1)) -// .subscribe((res) => (result = res)); - -// // 1*1*300 = 300 -// tick(300); -// expect(result).toEqual(undefined); - -// // 2*2*300 = 1200 -// tick(1200); - -// expect(result).toEqual(mockResult); -// subscription.unsubscribe(); -// })); - -// it(`should unsuccessfully backOff on 500 error`, fakeAsync(() => { -// spyOn(httpClient, 'post').and.returnValue(throwError(mock500Error)); - -// let result: HttpErrorModel | undefined; -// const subscription = service -// .verifyPayment(mockPaymentSessionId, mockPayload) -// .subscribe({ error: (err) => (result = err) }); - -// tick(4200); - -// expect(result).toEqual(mockNormalized500Error); - -// subscription.unsubscribe(); -// })); -// }); -// }); -// }); +import { HttpErrorResponse } from '@angular/common/http'; +import { + HttpClientTestingModule, + HttpTestingController, +} from '@angular/common/http/testing'; +import { TestBed } from '@angular/core/testing'; +import { ConverterService, LoggerService } from '@spartacus/core'; +import { OpfApiBaseAdapter } from './opf-api-base.adapter'; +import { OpfEndpointsService } from '@spartacus/opf/base/core'; +import { + ActiveConfiguration, + OPF_CC_PUBLIC_KEY_HEADER, + OpfConfig, + OpfPaymentProviderType, +} from '@spartacus/opf/base/root'; +import { map } from 'rxjs'; + +const mockActiveConfigurations: ActiveConfiguration[] = [ + { + id: 1, + description: 'First active configuration', + merchantId: 'merchant-123', + providerType: OpfPaymentProviderType.PAYMENT_GATEWAY, + displayName: 'Payment Gateway 1', + acquirerCountryCode: 'US', + }, + { + id: 2, + description: 'Second active configuration', + merchantId: 'merchant-456', + providerType: OpfPaymentProviderType.PAYMENT_METHOD, + displayName: 'Payment Method 2', + acquirerCountryCode: 'CA', + }, +]; + +const mockErrorResponse = new HttpErrorResponse({ + error: 'test 404 error', + status: 404, + statusText: 'Not Found', +}); + +class MockLoggerService implements Partial { + log(): void {} + warn(): void {} + error(): void {} + info(): void {} + debug(): void {} +} + +const mockOpfConfig: OpfConfig = { + opf: { + commerceCloudPublicKey: 'test-public-key', + }, +}; + +class MockOpfEndpointsService implements Partial { + buildUrl(endpoint: string): string { + return `test-url/${endpoint}`; + } +} + +describe('OpfApiBaseAdapter', () => { + let service: OpfApiBaseAdapter; + let httpMock: HttpTestingController; + let converter: ConverterService; + let opfEndpointsService: OpfEndpointsService; + let logger: LoggerService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + OpfApiBaseAdapter, + ConverterService, + { provide: LoggerService, useClass: MockLoggerService }, + { provide: OpfEndpointsService, useClass: MockOpfEndpointsService }, + { provide: OpfConfig, useValue: mockOpfConfig }, + ], + }); + + service = TestBed.inject(OpfApiBaseAdapter); + httpMock = TestBed.inject(HttpTestingController); + converter = TestBed.inject(ConverterService); + opfEndpointsService = TestBed.inject(OpfEndpointsService); + logger = TestBed.inject(LoggerService); + + spyOn(converter, 'pipeable').and.returnValue( + map(() => mockActiveConfigurations) + ); + spyOn(logger, 'error').and.callThrough(); + }); + + afterEach(() => { + httpMock.verify(); + }); + + it('should be created', () => { + if (opfEndpointsService) { + } + expect(service).toBeTruthy(); + }); + + it('should fetch active configurations successfully', () => { + service.getActiveConfigurations().subscribe((result) => { + expect(result).toEqual(mockActiveConfigurations); + }); + + const req = httpMock.expectOne('test-url/getActiveConfigurations'); + expect(req.request.method).toBe('GET'); + expect(req.request.headers.get('Accept-Language')).toBe('en-us'); + expect(req.request.headers.get(OPF_CC_PUBLIC_KEY_HEADER)).toBe( + 'test-public-key' + ); + + req.flush(mockActiveConfigurations); + }); + + it('should handle http errors when fetching active configurations', () => { + service.getActiveConfigurations().subscribe({ + error: (error) => { + expect(error).toBeTruthy(); + }, + }); + + const req = httpMock.expectOne('test-url/getActiveConfigurations'); + req.flush(mockErrorResponse, { status: 404, statusText: 'Not Found' }); + }); +}); diff --git a/integration-libs/opf/cta/core/facade/opf-cta.service.spec.ts b/integration-libs/opf/cta/core/facade/opf-cta.service.spec.ts index 75f3d3024ec..df5f9d6588c 100644 --- a/integration-libs/opf/cta/core/facade/opf-cta.service.spec.ts +++ b/integration-libs/opf/cta/core/facade/opf-cta.service.spec.ts @@ -4,4 +4,101 @@ * SPDX-License-Identifier: Apache-2.0 */ -// TODO: Add unit tests... +import { TestBed } from '@angular/core/testing'; +import { CommandService } from '@spartacus/core'; +import { CtaScriptsRequest, CtaScriptsResponse } from '@spartacus/opf/cta/root'; +import { Observable, of } from 'rxjs'; +import { OpfCtaService } from './opf-cta.service'; +import { OpfCtaConnector } from '../connectors/opf-cta.connector'; + +class MockCommandService { + create(fn: (payload: any) => Observable) { + return { + execute: (payload: any) => fn(payload), + }; + } +} + +const ctaScriptsResponseMock: CtaScriptsResponse = { + value: [ + { + paymentAccountId: 1, + dynamicScript: { + html: '

Thanks for purchasing our great products

Please use promo code:123abc for your next purchase

', + cssUrls: [ + { + url: 'https://checkoutshopper-test.adyen.com/checkoutshopper/sdk/4.2.1/adyen.css', + sri: '', + }, + ], + jsUrls: [ + { + url: 'https://checkoutshopper-test.adyen.com/checkoutshopper/sdk/4.2.1/adyen.js', + sri: '', + }, + ], + }, + }, + ], +}; + +class MockOpfCtaConnector { + getCtaScripts( + _ctaScriptsRequest: CtaScriptsRequest + ): Observable { + return of(ctaScriptsResponseMock); + } +} + +describe('OpfCtaService', () => { + let service: OpfCtaService; + let commandService: MockCommandService; + let opfCtaConnector: MockOpfCtaConnector; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + OpfCtaService, + { provide: CommandService, useClass: MockCommandService }, + { provide: OpfCtaConnector, useClass: MockOpfCtaConnector }, + ], + }); + + service = TestBed.inject(OpfCtaService); + commandService = TestBed.inject(CommandService); + opfCtaConnector = TestBed.inject(OpfCtaConnector); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should execute getCtaScripts and return response', (done) => { + spyOn(opfCtaConnector, 'getCtaScripts').and.callThrough(); + spyOn(commandService, 'create').and.callThrough(); + const request: CtaScriptsRequest = { + paymentAccountIds: [1], + cartId: 'cart123', + ctaProductItems: [{ productId: 'prod123', quantity: 1 }], + }; + + service.getCtaScripts(request).subscribe((response: CtaScriptsResponse) => { + expect(response).toEqual(ctaScriptsResponseMock); + expect(opfCtaConnector.getCtaScripts).toHaveBeenCalledWith(request); + done(); + }); + }); + + it('should emit script ready event', () => { + let emittedValue: string | undefined; + + service.listenScriptReadyEvent().subscribe((value) => { + emittedValue = value; + }); + + const scriptIdentifier = 'script1'; + service.emitScriptReadyEvent(scriptIdentifier); + + expect(emittedValue).toBe(scriptIdentifier); + }); +}); diff --git a/integration-libs/opf/cta/opf-api/adapters/opf-api-cta.adapter.spec.ts b/integration-libs/opf/cta/opf-api/adapters/opf-api-cta.adapter.spec.ts index a6104de5d25..432ca5c5bbc 100644 --- a/integration-libs/opf/cta/opf-api/adapters/opf-api-cta.adapter.spec.ts +++ b/integration-libs/opf/cta/opf-api/adapters/opf-api-cta.adapter.spec.ts @@ -4,4 +4,141 @@ * SPDX-License-Identifier: Apache-2.0 */ -// TODO: Add unit tests +import { + HttpClientTestingModule, + HttpTestingController, +} from '@angular/common/http/testing'; +import { TestBed } from '@angular/core/testing'; +import { ConverterService, LoggerService } from '@spartacus/core'; +import { OpfApiCtaAdapter } from './opf-api-cta.adapter'; +import { OpfEndpointsService } from '@spartacus/opf/base/core'; +import { + OPF_CC_PUBLIC_KEY_HEADER, + OpfConfig, + OpfDynamicScriptResourceType, +} from '@spartacus/opf/base/root'; +import { CtaScriptsRequest, CtaScriptsResponse } from '@spartacus/opf/cta/root'; +import { Observable, of } from 'rxjs'; +import { catchError } from 'rxjs/operators'; + +const mockCtaScriptsRequest: CtaScriptsRequest = { + paymentAccountIds: [123], + cartId: 'mockCartId', + additionalData: [ + { key: 'divisionId', value: 'mockDivision' }, + { key: 'currency', value: 'USD' }, + ], +}; + +const mockCtaScriptsResponse: CtaScriptsResponse = { + value: [ + { + paymentAccountId: 123, + dynamicScript: { + html: '', + jsUrls: [ + { + url: 'script.js', + type: OpfDynamicScriptResourceType.SCRIPT, + }, + ], + cssUrls: [ + { + url: 'styles.css', + type: OpfDynamicScriptResourceType.STYLES, + }, + ], + }, + }, + ], +}; + +const mockOpfConfig: OpfConfig = { + opf: { + commerceCloudPublicKey: 'mockPublicKey', + }, +}; + +describe('OpfApiCtaAdapter', () => { + let adapter: OpfApiCtaAdapter; + let httpMock: HttpTestingController; + let converterService: jasmine.SpyObj; + let opfEndpointsService: jasmine.SpyObj; + + beforeEach(() => { + const converterSpy = jasmine.createSpyObj('ConverterService', ['pipeable']); + const opfEndpointsSpy = jasmine.createSpyObj('OpfEndpointsService', [ + 'buildUrl', + ]); + + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + OpfApiCtaAdapter, + { provide: OpfConfig, useValue: mockOpfConfig }, + { provide: ConverterService, useValue: converterSpy }, + { provide: OpfEndpointsService, useValue: opfEndpointsSpy }, + LoggerService, + ], + }); + + adapter = TestBed.inject(OpfApiCtaAdapter); + httpMock = TestBed.inject(HttpTestingController); + converterService = TestBed.inject( + ConverterService + ) as jasmine.SpyObj; + opfEndpointsService = TestBed.inject( + OpfEndpointsService + ) as jasmine.SpyObj; + + opfEndpointsService.buildUrl.and.returnValue('mockUrl'); + }); + + afterEach(() => { + httpMock.verify(); + }); + + it('should fetch CTA scripts successfully', () => { + converterService.pipeable.and.returnValue( + (input$: Observable) => input$ + ); + + adapter.getCtaScripts(mockCtaScriptsRequest).subscribe((response) => { + expect(response).toEqual(mockCtaScriptsResponse); + }); + + const req = httpMock.expectOne('mockUrl'); + expect(req.request.method).toBe('POST'); + expect(req.request.headers.get(OPF_CC_PUBLIC_KEY_HEADER)).toBe( + 'mockPublicKey' + ); + expect(req.request.headers.get('Accept-Language')).toBe('en-us'); + expect(req.request.body).toEqual(mockCtaScriptsRequest); + + req.flush(mockCtaScriptsResponse); + }); + + it('should handle server error and normalize the error', () => { + const mockErrorResponse = { + status: 500, + statusText: 'Internal Server Error', + }; + + converterService.pipeable.and.returnValue( + (input$: Observable) => input$ + ); + + adapter + .getCtaScripts(mockCtaScriptsRequest) + .pipe( + catchError((error) => { + expect(error).toEqual(jasmine.any(Error)); + return of(null); + }) + ) + .subscribe(); + + const req = httpMock.expectOne('mockUrl'); + req.flush(null, mockErrorResponse); + }); +}); diff --git a/integration-libs/opf/payment/core/facade/opf-payment.service.spec.ts b/integration-libs/opf/payment/core/facade/opf-payment.service.spec.ts index 05238bb45d9..d453c46fa10 100644 --- a/integration-libs/opf/payment/core/facade/opf-payment.service.spec.ts +++ b/integration-libs/opf/payment/core/facade/opf-payment.service.spec.ts @@ -4,326 +4,254 @@ * SPDX-License-Identifier: Apache-2.0 */ -// TODO: Add unit tests.. - -// import { TestBed } from '@angular/core/testing'; -// import { CommandService, QueryService } from '@spartacus/core'; -// import { Observable, of } from 'rxjs'; -// import { -// ActiveConfiguration, -// AfterRedirectScriptResponse, -// CtaScriptsLocation, -// CtaScriptsRequest, -// CtaScriptsResponse, -// OpfPaymentProviderType, -// OpfPaymentVerificationPayload, -// OpfPaymentVerificationResponse, -// SubmitCompleteInput, -// SubmitInput, -// } from '../../root/model'; -// import { OpfPaymentConnector } from '../connectors'; -// import { OpfPaymentHostedFieldsService } from '../services'; -// import { OpfPaymentService } from './opf-payment.service'; - -// class MockPaymentConnector implements Partial { -// verifyPayment( -// _paymentSessionId: string, -// _payload: OpfPaymentVerificationPayload -// ): Observable { -// return of({ -// result: 'result', -// }) as Observable; -// } -// afterRedirectScripts( -// _paymentSessionId: string -// ): Observable { -// return of({ afterRedirectScript: {} }); -// } -// getActiveConfigurations(): Observable { -// return of(mockActiveConfigurations); -// } -// getCtaScripts( -// _ctaScriptsRequest: CtaScriptsRequest -// ): Observable { -// return of(MockCtaScriptsResponse); -// } -// } - -// class MockOpfPaymentHostedFieldsService { -// submitPayment(): Observable { -// return of(true); -// } - -// submitCompletePayment(): Observable { -// return of(true); -// } -// } - -// const mockSubmitInput = { -// cartId: '123', -// } as SubmitInput; - -// const mockActiveConfigurations: ActiveConfiguration[] = [ -// { -// id: 1, -// providerType: OpfPaymentProviderType.PAYMENT_GATEWAY, -// displayName: 'Test1', -// }, -// { -// id: 2, -// providerType: OpfPaymentProviderType.PAYMENT_METHOD, -// displayName: 'Test2', -// }, -// ]; - -// const MockCtaRequest: CtaScriptsRequest = { -// cartId: '00259012', -// ctaProductItems: [ -// { -// productId: '301233', -// quantity: 1, -// }, -// { -// productId: '2231913', -// quantity: 1, -// }, -// { -// productId: '1776948', -// quantity: 1, -// }, -// ], -// scriptLocations: [CtaScriptsLocation.ORDER_HISTORY_PAYMENT_GUIDE], -// }; - -// const MockCtaScriptsResponse: CtaScriptsResponse = { -// value: [ -// { -// paymentAccountId: 1, -// dynamicScript: { -// html: "

CTA Html snippet #1

", -// cssUrls: [ -// { -// url: 'https://checkoutshopper-test.adyen.com/checkoutshopper/sdk/4.2.1/adyen.css', -// sri: '', -// }, -// ], -// jsUrls: [ -// { -// url: 'https://checkoutshopper-test.adyen.com/checkoutshopper/sdk/4.2.1/adyen.js', -// sri: '', -// }, -// ], -// }, -// }, -// ], -// }; - -// describe('OpfPaymentService', () => { -// let service: OpfPaymentService; -// let paymentConnector: MockPaymentConnector; -// let opfPaymentHostedFieldsServiceSpy: OpfPaymentHostedFieldsService; - -// beforeEach(() => { -// TestBed.configureTestingModule({ -// providers: [ -// OpfPaymentService, -// QueryService, -// CommandService, -// { -// provide: OpfPaymentConnector, -// useClass: MockPaymentConnector, -// }, -// { -// provide: OpfPaymentHostedFieldsService, -// useClass: MockOpfPaymentHostedFieldsService, -// }, -// ], -// }); - -// service = TestBed.inject(OpfPaymentService); -// paymentConnector = TestBed.inject(OpfPaymentConnector); -// opfPaymentHostedFieldsServiceSpy = TestBed.inject( -// OpfPaymentHostedFieldsService -// ); -// }); - -// it('should be created', () => { -// expect(service).toBeTruthy(); -// }); - -// it('should call submitPayment with proper payload after submitPaymentCommand execution', () => { -// const submitPaymentSpy = spyOn( -// opfPaymentHostedFieldsServiceSpy, -// 'submitPayment' -// ).and.callThrough(); - -// const submitInput: SubmitInput = { -// cartId: 'testCart', -// } as SubmitInput; - -// service['submitPaymentCommand'].execute({ submitInput }); - -// expect(submitPaymentSpy).toHaveBeenCalledWith(submitInput); -// }); - -// it('should call verifyPayment with proper payload after verifyPaymentCommand execution', () => { -// const verifyPaymentSpy = spyOn( -// paymentConnector, -// 'verifyPayment' -// ).and.callThrough(); - -// const verifyPaymentPayload = { -// paymentSessionId: 'exampleSessionId', -// paymentVerificationPayload: { -// responseMap: [], -// }, -// }; - -// service['verifyPaymentCommand'].execute(verifyPaymentPayload); - -// expect(verifyPaymentSpy).toHaveBeenCalledWith( -// verifyPaymentPayload.paymentSessionId, -// verifyPaymentPayload.paymentVerificationPayload -// ); -// }); - -// it('should call submitCompletePayment with proper payload after submitCompletePaymentCommand execution', () => { -// const submitCompletePaymentSpy = spyOn( -// opfPaymentHostedFieldsServiceSpy, -// 'submitCompletePayment' -// ).and.callThrough(); - -// const submitCompleteInput: SubmitCompleteInput = { -// cartId: 'testCart', -// } as SubmitCompleteInput; - -// service['submitCompletePaymentCommand'].execute({ submitCompleteInput }); - -// expect(submitCompletePaymentSpy).toHaveBeenCalledWith(submitCompleteInput); -// }); - -// it('should call verifyPayment from connector with the correct payload', () => { -// const paymentSessionId = 'exampleSessionId'; -// const paymentVerificationPayload = { -// responseMap: [ -// { -// key: 'key', -// value: 'value', -// }, -// ], -// } as OpfPaymentVerificationPayload; -// const connectorVerifySpy = spyOn( -// paymentConnector, -// 'verifyPayment' -// ).and.callThrough(); - -// service.verifyPayment(paymentSessionId, paymentVerificationPayload); - -// expect(connectorVerifySpy).toHaveBeenCalledWith( -// paymentSessionId, -// paymentVerificationPayload -// ); -// }); - -// it('should call submitPayment from opfPaymentHostedFieldsService with the correct payload', () => { -// const submitPaymentSpy = spyOn( -// opfPaymentHostedFieldsServiceSpy, -// 'submitPayment' -// ).and.callThrough(); - -// service.submitPayment(mockSubmitInput); - -// expect(submitPaymentSpy).toHaveBeenCalledWith(mockSubmitInput); -// }); - -// it('should call submitCompletePayment from opfPaymentHostedFieldsService with the correct payload', () => { -// const submitCompletePaymentSpy = spyOn( -// opfPaymentHostedFieldsServiceSpy, -// 'submitCompletePayment' -// ).and.callThrough(); - -// service.submitCompletePayment(mockSubmitInput); - -// expect(submitCompletePaymentSpy).toHaveBeenCalledWith(mockSubmitInput); -// }); - -// it('should return true when payment submission is successful', (done) => { -// const result = service.submitPayment(mockSubmitInput); - -// result.subscribe((response) => { -// expect(response).toBe(true); -// done(); -// }); -// }); - -// it('should return a successful payment verification response', (done) => { -// const paymentSessionId = 'exampleSessionId'; -// const paymentVerificationPayload = { -// responseMap: [ -// { -// key: 'key', -// value: 'value', -// }, -// ], -// } as OpfPaymentVerificationPayload; - -// const expectedResult = { -// result: 'result', -// } as OpfPaymentVerificationResponse; - -// const result = service.verifyPayment( -// paymentSessionId, -// paymentVerificationPayload -// ); - -// result.subscribe((response) => { -// expect(response).toEqual(expectedResult); -// done(); -// }); -// }); - -// it('should return true when payment submission is completed successfully', (done) => { -// const result = service.submitCompletePayment(mockSubmitInput); - -// result.subscribe((response) => { -// expect(response).toBe(true); -// done(); -// }); -// }); - -// it('should call afterRedirectScripts from connector with the correct payload', () => { -// const paymentSessionId = 'exampleSessionId'; - -// const connectorSpy = spyOn( -// paymentConnector, -// 'afterRedirectScripts' -// ).and.callThrough(); - -// service.afterRedirectScripts(paymentSessionId); - -// expect(connectorSpy).toHaveBeenCalledWith(paymentSessionId); -// }); - -// it(`should return mockActiveConfigurations data`, (done) => { -// service.getActiveConfigurationsState().subscribe((state) => { -// expect(state).toEqual({ -// loading: false, -// error: false, -// data: mockActiveConfigurations, -// }); -// done(); -// }); -// }); - -// it(`should return ctaScripts data`, (done) => { -// const connectorCtaSpy = spyOn( -// paymentConnector, -// 'getCtaScripts' -// ).and.callThrough(); - -// service.getCtaScripts(MockCtaRequest).subscribe(() => { -// expect(connectorCtaSpy).toHaveBeenCalledWith(MockCtaRequest); -// done(); -// }); -// }); -// }); +import { TestBed } from '@angular/core/testing'; +import { CommandService, QueryService } from '@spartacus/core'; +import { Observable, of } from 'rxjs'; +import { + AfterRedirectScriptResponse, + OpfPaymentVerificationPayload, + OpfPaymentVerificationResponse, + SubmitCompleteInput, + SubmitInput, +} from '../../root/model'; +import { OpfPaymentConnector } from '../connectors'; +import { OpfPaymentHostedFieldsService } from '../services'; +import { OpfPaymentService } from './opf-payment.service'; + +class MockPaymentConnector implements Partial { + verifyPayment( + _paymentSessionId: string, + _payload: OpfPaymentVerificationPayload + ): Observable { + return of({ + result: 'result', + }) as Observable; + } + afterRedirectScripts( + _paymentSessionId: string + ): Observable { + return of({ afterRedirectScript: {} }); + } +} + +class MockOpfPaymentHostedFieldsService { + submitPayment(): Observable { + return of(true); + } + + submitCompletePayment(): Observable { + return of(true); + } +} + +const mockSubmitInput = { + cartId: '123', +} as SubmitInput; + +const mockSubmitCompleteInput: SubmitCompleteInput = { + cartId: 'mockCartId', + additionalData: [{ key: 'key', value: 'value' }], + paymentSessionId: 'sessionId', + returnPath: 'checkout', + callbackArray: [() => {}, () => {}, () => {}], +}; + +describe('OpfPaymentService', () => { + let service: OpfPaymentService; + let paymentConnector: MockPaymentConnector; + let opfPaymentHostedFieldsServiceSpy: OpfPaymentHostedFieldsService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + OpfPaymentService, + QueryService, + CommandService, + { + provide: OpfPaymentConnector, + useClass: MockPaymentConnector, + }, + { + provide: OpfPaymentHostedFieldsService, + useClass: MockOpfPaymentHostedFieldsService, + }, + ], + }); + + service = TestBed.inject(OpfPaymentService); + paymentConnector = TestBed.inject(OpfPaymentConnector); + opfPaymentHostedFieldsServiceSpy = TestBed.inject( + OpfPaymentHostedFieldsService + ); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should call submitPayment with proper payload after submitPaymentCommand execution', () => { + const submitPaymentSpy = spyOn( + opfPaymentHostedFieldsServiceSpy, + 'submitPayment' + ).and.callThrough(); + + const submitInput: SubmitInput = { + cartId: 'testCart', + } as SubmitInput; + + service['submitPaymentCommand'].execute({ submitInput }); + + expect(submitPaymentSpy).toHaveBeenCalledWith(submitInput); + }); + + it('should call verifyPayment with proper payload after verifyPaymentCommand execution', () => { + const verifyPaymentSpy = spyOn( + paymentConnector, + 'verifyPayment' + ).and.callThrough(); + + const verifyPaymentPayload = { + paymentSessionId: 'exampleSessionId', + paymentVerificationPayload: { + responseMap: [], + }, + }; + + service['verifyPaymentCommand'].execute(verifyPaymentPayload); + + expect(verifyPaymentSpy).toHaveBeenCalledWith( + verifyPaymentPayload.paymentSessionId, + verifyPaymentPayload.paymentVerificationPayload + ); + }); + + it('should call submitCompletePayment with proper payload after submitCompletePaymentCommand execution', () => { + const submitCompletePaymentSpy = spyOn( + opfPaymentHostedFieldsServiceSpy, + 'submitCompletePayment' + ).and.callThrough(); + + const submitCompleteInput: SubmitCompleteInput = { + cartId: 'testCart', + } as SubmitCompleteInput; + + service['submitCompletePaymentCommand'].execute({ submitCompleteInput }); + + expect(submitCompletePaymentSpy).toHaveBeenCalledWith(submitCompleteInput); + }); + + it('should call verifyPayment from connector with the correct payload', () => { + const paymentSessionId = 'exampleSessionId'; + const paymentVerificationPayload = { + responseMap: [ + { + key: 'key', + value: 'value', + }, + ], + } as OpfPaymentVerificationPayload; + const connectorVerifySpy = spyOn( + paymentConnector, + 'verifyPayment' + ).and.callThrough(); + + service.verifyPayment(paymentSessionId, paymentVerificationPayload); + + expect(connectorVerifySpy).toHaveBeenCalledWith( + paymentSessionId, + paymentVerificationPayload + ); + }); + + it('should call submitPayment from opfPaymentHostedFieldsService with the correct payload', () => { + const submitPaymentSpy = spyOn( + opfPaymentHostedFieldsServiceSpy, + 'submitPayment' + ).and.callThrough(); + + service.submitPayment(mockSubmitInput); + + expect(submitPaymentSpy).toHaveBeenCalledWith(mockSubmitInput); + }); + + it('should call submitCompletePayment from opfPaymentHostedFieldsService with the correct payload', () => { + const submitCompletePaymentSpy = spyOn( + opfPaymentHostedFieldsServiceSpy, + 'submitCompletePayment' + ).and.callThrough(); + + service.submitCompletePayment(mockSubmitCompleteInput); + + expect(submitCompletePaymentSpy).toHaveBeenCalledWith( + mockSubmitCompleteInput + ); + }); + + it('should return true when payment submission is successful', (done) => { + const result = service.submitPayment(mockSubmitInput); + + result.subscribe((response) => { + expect(response).toBe(true); + done(); + }); + }); + + it('should return a successful payment verification response', (done) => { + const paymentSessionId = 'exampleSessionId'; + const paymentVerificationPayload = { + responseMap: [ + { + key: 'key', + value: 'value', + }, + ], + } as OpfPaymentVerificationPayload; + + const expectedResult = { + result: 'result', + } as OpfPaymentVerificationResponse; + + const result = service.verifyPayment( + paymentSessionId, + paymentVerificationPayload + ); + + result.subscribe((response) => { + expect(response).toEqual(expectedResult); + done(); + }); + }); + + it('should return true when payment submission is completed successfully', (done) => { + const result = service.submitCompletePayment(mockSubmitCompleteInput); + + result.subscribe((response) => { + expect(response).toBe(true); + done(); + }); + }); + + it('should call afterRedirectScripts from connector with the correct payload', () => { + const paymentSessionId = 'exampleSessionId'; + + const connectorSpy = spyOn( + paymentConnector, + 'afterRedirectScripts' + ).and.callThrough(); + + service.afterRedirectScripts(paymentSessionId); + + expect(connectorSpy).toHaveBeenCalledWith(paymentSessionId); + }); + + // const connectorCtaSpy = spyOn( + // paymentConnector, + // 'getCtaScripts' + // ).and.callThrough(); + + // service.getCtaScripts(MockCtaRequest).subscribe(() => { + // expect(connectorCtaSpy).toHaveBeenCalledWith(MockCtaRequest); + // done(); + // }); + // }); +}); diff --git a/integration-libs/opf/payment/opf-api/adapters/opf-api-payment.adapter.spec.ts b/integration-libs/opf/payment/opf-api/adapters/opf-api-payment.adapter.spec.ts index 24c31333528..30146adebe6 100644 --- a/integration-libs/opf/payment/opf-api/adapters/opf-api-payment.adapter.spec.ts +++ b/integration-libs/opf/payment/opf-api/adapters/opf-api-payment.adapter.spec.ts @@ -4,245 +4,222 @@ * SPDX-License-Identifier: Apache-2.0 */ -// TODO: Add unit tests... - -// import { -// HttpClient, -// HttpErrorResponse, -// HttpHeaders, -// } from '@angular/common/http'; -// import { -// HttpClientTestingModule, -// HttpTestingController, -// } from '@angular/common/http/testing'; -// import { fakeAsync, TestBed, tick } from '@angular/core/testing'; -// import { -// BaseOccUrlProperties, -// ConverterService, -// DynamicAttributes, -// HttpErrorModel, -// normalizeHttpError, -// } from '@spartacus/core'; -// import { defer, of, throwError } from 'rxjs'; -// import { take } from 'rxjs/operators'; -// import { OpfEndpointsService } from '../../core/services'; -// import { OPF_PAYMENT_VERIFICATION_NORMALIZER } from '../../core/tokens'; -// import { OpfConfig } from '../../root/config'; -// import { OpfPaymentVerificationResponse } from '../../root/model'; -// import { OccOpfPaymentAdapter } from './occ-opf.adapter'; - -// const mockJaloError = new HttpErrorResponse({ -// error: { -// errors: [ -// { -// message: 'The application has encountered an error', -// type: 'JaloObjectNoLongerValidError', -// }, -// ], -// }, -// }); - -// const mockOpfConfig: OpfConfig = {}; - -// const mockPayload = { -// responseMap: [ -// { -// key: 'key', -// value: 'value', -// }, -// ], -// }; - -// const mockResult: OpfPaymentVerificationResponse = { -// result: 'mockResult', -// }; - -// export class MockOpfEndpointsService implements Partial { -// buildUrl( -// endpoint: string, -// _attributes?: DynamicAttributes, -// _propertiesToOmit?: BaseOccUrlProperties -// ) { -// return this.getEndpoint(endpoint); -// } -// getEndpoint(endpoint: string) { -// if (!endpoint.startsWith('/')) { -// endpoint = '/' + endpoint; -// } -// return endpoint; -// } -// } - -// const mockPaymentSessionId = '123'; - -// const mockNormalizedJaloError = normalizeHttpError(mockJaloError); - -// const mock500Error = new HttpErrorResponse({ -// error: 'error', -// headers: new HttpHeaders().set('xxx', 'xxx'), -// status: 500, -// statusText: 'Unknown error', -// url: '/xxx', -// }); - -// const mockNormalized500Error = normalizeHttpError(mock500Error); - -// describe(`OccOpfPaymentAdapter`, () => { -// let service: OccOpfPaymentAdapter; -// let httpMock: HttpTestingController; -// let converter: ConverterService; -// let opfEndpointsService: OpfEndpointsService; -// let httpClient: HttpClient; - -// beforeEach(() => { -// TestBed.configureTestingModule({ -// imports: [HttpClientTestingModule], -// providers: [ -// OccOpfPaymentAdapter, -// { -// provide: OpfEndpointsService, -// useClass: MockOpfEndpointsService, -// }, -// { -// provide: OpfConfig, -// useValue: mockOpfConfig, -// }, -// ], -// }); - -// service = TestBed.inject(OccOpfPaymentAdapter); -// httpMock = TestBed.inject(HttpTestingController); -// httpClient = TestBed.inject(HttpClient); -// converter = TestBed.inject(ConverterService); -// opfEndpointsService = TestBed.inject(OpfEndpointsService); -// spyOn(converter, 'convert').and.callThrough(); -// spyOn(converter, 'pipeable').and.callThrough(); -// spyOn(opfEndpointsService, 'buildUrl').and.callThrough(); -// }); - -// afterEach(() => { -// httpMock.verify(); -// }); - -// it('should be created', () => { -// expect(service).toBeTruthy(); -// }); - -// describe(`verifyPayment`, () => { -// it(`should get all supported delivery modes for cart for given user id and cart id`, (done) => { -// service -// .verifyPayment(mockPaymentSessionId, mockPayload) -// .pipe(take(1)) -// .subscribe((result) => { -// expect(result).toEqual(mockResult); -// done(); -// }); - -// const url = service['verifyPaymentEndpoint'](mockPaymentSessionId); -// const mockReq = httpMock.expectOne(url); - -// expect(mockReq.cancelled).toBeFalsy(); -// expect(mockReq.request.responseType).toEqual('json'); -// mockReq.flush(mockResult); -// expect(converter.pipeable).toHaveBeenCalledWith( -// OPF_PAYMENT_VERIFICATION_NORMALIZER -// ); -// }); - -// describe(`back-off`, () => { -// it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { -// spyOn(httpClient, 'post').and.returnValue(throwError(mockJaloError)); - -// let result: HttpErrorModel | undefined; -// const subscription = service -// .verifyPayment(mockPaymentSessionId, mockPayload) -// .subscribe({ error: (err) => (result = err) }); - -// tick(4200); - -// expect(result).toEqual(mockNormalizedJaloError); - -// subscription.unsubscribe(); -// })); - -// it(`should successfully backOff on Jalo error and recover after the third retry`, fakeAsync(() => { -// let calledTimes = -1; - -// spyOn(httpClient, 'post').and.returnValue( -// defer(() => { -// calledTimes++; -// if (calledTimes === 3) { -// return of(mockResult); -// } -// return throwError(mockJaloError); -// }) -// ); - -// let result: OpfPaymentVerificationResponse | undefined; -// const subscription = service -// .verifyPayment(mockPaymentSessionId, mockPayload) -// .pipe(take(1)) -// .subscribe((res) => (result = res)); - -// // 1*1*300 = 300 -// tick(300); -// expect(result).toEqual(undefined); - -// // 2*2*300 = 1200 -// tick(1200); -// expect(result).toEqual(undefined); - -// // 3*3*300 = 2700 -// tick(2700); - -// expect(result).toEqual(mockResult); -// subscription.unsubscribe(); -// })); - -// it(`should successfully backOff on 500 error and recover after the 2nd retry`, fakeAsync(() => { -// let calledTimes = -1; - -// spyOn(httpClient, 'post').and.returnValue( -// defer(() => { -// calledTimes++; -// if (calledTimes === 2) { -// return of(mockResult); -// } -// return throwError(mock500Error); -// }) -// ); - -// let result: OpfPaymentVerificationResponse | undefined; -// const subscription = service -// .verifyPayment(mockPaymentSessionId, mockPayload) -// .pipe(take(1)) -// .subscribe((res) => (result = res)); - -// // 1*1*300 = 300 -// tick(300); -// expect(result).toEqual(undefined); - -// // 2*2*300 = 1200 -// tick(1200); - -// expect(result).toEqual(mockResult); -// subscription.unsubscribe(); -// })); - -// it(`should unsuccessfully backOff on 500 error`, fakeAsync(() => { -// spyOn(httpClient, 'post').and.returnValue(throwError(mock500Error)); - -// let result: HttpErrorModel | undefined; -// const subscription = service -// .verifyPayment(mockPaymentSessionId, mockPayload) -// .subscribe({ error: (err) => (result = err) }); - -// tick(4200); - -// expect(result).toEqual(mockNormalized500Error); - -// subscription.unsubscribe(); -// })); -// }); -// }); -// }); +import { + HttpClient, + HttpErrorResponse, + HttpHeaders, +} from '@angular/common/http'; +import { + HttpClientTestingModule, + HttpTestingController, +} from '@angular/common/http/testing'; +import { fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { + BaseOccUrlProperties, + ConverterService, + DynamicAttributes, + HttpErrorModel, + LoggerService, + tryNormalizeHttpError, +} from '@spartacus/core'; +import { defer, of, throwError } from 'rxjs'; +import { take } from 'rxjs/operators'; +import { OpfEndpointsService } from '@spartacus/opf/base/core'; +import { OPF_PAYMENT_VERIFICATION_NORMALIZER } from '../../core/tokens'; +import { OpfConfig } from '@spartacus/opf/base/root'; +import { OpfPaymentVerificationResponse } from '../../root/model'; +import { OpfApiPaymentAdapter } from './opf-api-payment.adapter'; + +class MockLoggerService implements Partial { + log(): void {} + warn(): void {} + error(): void {} + info(): void {} + debug(): void {} +} + +const mockJaloError = new HttpErrorResponse({ + error: { + errors: [ + { + message: 'The application has encountered an error', + type: 'JaloObjectNoLongerValidError', + }, + ], + }, +}); + +const mockOpfConfig: OpfConfig = {}; + +const mockPayload = { + responseMap: [ + { + key: 'key', + value: 'value', + }, + ], +}; + +const mockResult: OpfPaymentVerificationResponse = { + result: 'mockResult', +}; + +export class MockOpfEndpointsService implements Partial { + buildUrl( + endpoint: string, + _attributes?: DynamicAttributes, + _propertiesToOmit?: BaseOccUrlProperties + ) { + return this.getEndpoint(endpoint); + } + getEndpoint(endpoint: string) { + if (!endpoint.startsWith('/')) { + endpoint = '/' + endpoint; + } + return endpoint; + } +} + +const mockPaymentSessionId = '123'; + +const mockNormalizedJaloError = tryNormalizeHttpError( + mockJaloError, + new MockLoggerService() +); + +const mock500Error = new HttpErrorResponse({ + error: 'error', + headers: new HttpHeaders().set('xxx', 'xxx'), + status: 500, + statusText: 'Unknown error', + url: '/xxx', +}); + +const mockNormalized500Error = tryNormalizeHttpError( + mock500Error, + new MockLoggerService() +); + +describe(`OpfApiPaymentAdapter`, () => { + let service: OpfApiPaymentAdapter; + let httpMock: HttpTestingController; + let converter: ConverterService; + let opfEndpointsService: OpfEndpointsService; + let httpClient: HttpClient; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + OpfApiPaymentAdapter, + { + provide: OpfEndpointsService, + useClass: MockOpfEndpointsService, + }, + { + provide: OpfConfig, + useValue: mockOpfConfig, + }, + ], + }); + + service = TestBed.inject(OpfApiPaymentAdapter); + httpMock = TestBed.inject(HttpTestingController); + httpClient = TestBed.inject(HttpClient); + converter = TestBed.inject(ConverterService); + opfEndpointsService = TestBed.inject(OpfEndpointsService); + spyOn(converter, 'convert').and.callThrough(); + spyOn(converter, 'pipeable').and.callThrough(); + spyOn(opfEndpointsService, 'buildUrl').and.callThrough(); + }); + + afterEach(() => { + httpMock.verify(); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + describe(`verifyPayment`, () => { + it(`should get all supported delivery modes for cart for given user id and cart id`, (done) => { + service + .verifyPayment(mockPaymentSessionId, mockPayload) + .pipe(take(1)) + .subscribe((result) => { + expect(result).toEqual(mockResult); + done(); + }); + + const url = service['verifyPaymentEndpoint'](mockPaymentSessionId); + const mockReq = httpMock.expectOne(url); + + expect(mockReq.cancelled).toBeFalsy(); + expect(mockReq.request.responseType).toEqual('json'); + mockReq.flush(mockResult); + expect(converter.pipeable).toHaveBeenCalledWith( + OPF_PAYMENT_VERIFICATION_NORMALIZER + ); + }); + + describe(`back-off`, () => { + it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { + spyOn(httpClient, 'post').and.returnValue(throwError(mockJaloError)); + + let result: HttpErrorModel | undefined; + const subscription = service + .verifyPayment(mockPaymentSessionId, mockPayload) + .subscribe({ error: (err) => (result = err) }); + + tick(4200); + + expect(result).toEqual(mockNormalizedJaloError); + + subscription.unsubscribe(); + })); + + it(`should successfully backOff on 500 error and recover after the 2nd retry`, fakeAsync(() => { + let calledTimes = -1; + + spyOn(httpClient, 'post').and.returnValue( + defer(() => { + calledTimes++; + if (calledTimes === 2) { + return of(mockResult); + } + return throwError(mock500Error); + }) + ); + + let result: OpfPaymentVerificationResponse | undefined; + const subscription = service + .verifyPayment(mockPaymentSessionId, mockPayload) + .pipe(take(1)) + .subscribe((res) => (result = res)); + + tick(300); + expect(result).toEqual(undefined); + + tick(1200); + + expect(result).toEqual(mockResult); + subscription.unsubscribe(); + })); + + it(`should unsuccessfully backOff on 500 error`, fakeAsync(() => { + spyOn(httpClient, 'post').and.returnValue(throwError(mock500Error)); + + let result: HttpErrorModel | undefined; + const subscription = service + .verifyPayment(mockPaymentSessionId, mockPayload) + .subscribe({ error: (err) => (result = err) }); + + tick(4200); + + expect(result).toEqual(mockNormalized500Error); + + subscription.unsubscribe(); + })); + }); + }); +}); diff --git a/integration-libs/opf/quick-buy/opf-api/adapters/opf-api-quick-buy.adapter.spec.ts b/integration-libs/opf/quick-buy/opf-api/adapters/opf-api-quick-buy.adapter.spec.ts index 75f3d3024ec..b70811cda81 100644 --- a/integration-libs/opf/quick-buy/opf-api/adapters/opf-api-quick-buy.adapter.spec.ts +++ b/integration-libs/opf/quick-buy/opf-api/adapters/opf-api-quick-buy.adapter.spec.ts @@ -4,4 +4,125 @@ * SPDX-License-Identifier: Apache-2.0 */ -// TODO: Add unit tests... +import { + HttpClientTestingModule, + HttpTestingController, +} from '@angular/common/http/testing'; +import { TestBed } from '@angular/core/testing'; + +import { ConverterService, LoggerService } from '@spartacus/core'; +import { OpfEndpointsService } from '@spartacus/opf/base/core'; +import { + OPF_CC_ACCESS_CODE_HEADER, + OPF_CC_PUBLIC_KEY_HEADER, + OpfConfig, +} from '@spartacus/opf/base/root'; +import { OpfApiQuickBuyAdapter } from './opf-api-quick-buy.adapter'; +import { ApplePaySessionVerificationRequest } from '@spartacus/opf/quick-buy/root'; +import { catchError, Observable, throwError } from 'rxjs'; + +describe('OpfApiQuickBuyAdapter', () => { + let service: OpfApiQuickBuyAdapter; + let httpMock: HttpTestingController; + let mockConverter: jasmine.SpyObj; + let mockOpfEndpointsService: jasmine.SpyObj; + let mockOpfConfig: OpfConfig; + let mockLogger: jasmine.SpyObj; + + const applePaySessionRequest: ApplePaySessionVerificationRequest = { + validationUrl: 'https://example.com', + cartId: 'test', + initiative: 'web', + initiativeContext: 'test', + }; + + const accessCode = 'test-access-code'; + const mockResponse = { + epochTimestamp: 1625230000000, + expiresAt: 1625233600000, + merchantSessionIdentifier: 'merchant-session-id', + nonce: 'test-nonce', + merchantIdentifier: 'merchant.com.example', + domainName: 'example.com', + displayName: 'Example Merchant', + signature: 'test-signature', + }; + + beforeEach(() => { + mockConverter = jasmine.createSpyObj('ConverterService', ['pipeable']); + mockOpfEndpointsService = jasmine.createSpyObj('OpfEndpointsService', [ + 'buildUrl', + ]); + mockLogger = jasmine.createSpyObj('LoggerService', ['warn', 'error']); + mockOpfConfig = { opf: { commerceCloudPublicKey: 'public-key' } }; + + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + OpfApiQuickBuyAdapter, + { provide: ConverterService, useValue: mockConverter }, + { provide: OpfEndpointsService, useValue: mockOpfEndpointsService }, + { provide: OpfConfig, useValue: mockOpfConfig }, + { provide: LoggerService, useValue: mockLogger }, + ], + }); + + service = TestBed.inject(OpfApiQuickBuyAdapter); + httpMock = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + httpMock.verify(); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should call the correct URL and return the correct response', () => { + mockOpfEndpointsService.buildUrl.and.returnValue('/test-url'); + mockConverter.pipeable.and.callFake(() => { + return (source: Observable) => source; + }); + service + .getApplePayWebSession(applePaySessionRequest, accessCode) + .subscribe((response) => { + expect(response).toEqual(mockResponse); + }); + + const req = httpMock.expectOne('/test-url'); + expect(req.request.method).toBe('POST'); + + const headers = req.request.headers; + expect(headers.get('Content-Language')).toBe('en-us'); + expect(headers.get(OPF_CC_PUBLIC_KEY_HEADER)).toBe('public-key'); + expect(headers.get(OPF_CC_ACCESS_CODE_HEADER)).toBe(accessCode); + + req.flush(mockResponse); + }); + + it('should handle error and log it', () => { + mockOpfEndpointsService.buildUrl.and.returnValue('/test-url'); + const mockError = { status: 500, message: 'Server Error' }; + mockConverter.pipeable.and.callFake(() => { + return (source: Observable) => source; + }); + + service + .getApplePayWebSession(applePaySessionRequest, accessCode) + .pipe( + catchError((error) => { + expect(mockLogger.error).toHaveBeenCalled(); + return throwError(() => error); + }) + ) + .subscribe({ + error: (error) => { + expect(error.status).toBe(500); + }, + }); + + const req = httpMock.expectOne('/test-url'); + req.flush(mockError, { status: 500, statusText: 'Server Error' }); + }); +});