From 9c2139613d461a597c463a9b2430db3301e51290 Mon Sep 17 00:00:00 2001 From: Roman Chygryn <129765378+rmch91@users.noreply.github.com> Date: Mon, 14 Aug 2023 15:39:26 +0200 Subject: [PATCH] Unit tests for billing address (#17749) --- .../get-address-card-content.pipe.spec.ts | 116 +++++--- .../get-address-card-content.pipe.ts | 38 ++- ...out-billing-address-form.component.spec.ts | 275 ++++++++++-------- ...checkout-billing-address-form.component.ts | 1 - ...pf-checkout-billing-address-form.module.ts | 2 + ...ckout-billing-address-form.service.spec.ts | 196 +++++++++++++ 6 files changed, 454 insertions(+), 174 deletions(-) create mode 100644 integration-libs/opf/checkout/components/opf-checkout-billing-address-form/opf-checkout-billing-address-form.service.spec.ts diff --git a/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/get-address-card-content.pipe.spec.ts b/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/get-address-card-content.pipe.spec.ts index aa851408405..6d96e657c31 100644 --- a/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/get-address-card-content.pipe.spec.ts +++ b/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/get-address-card-content.pipe.spec.ts @@ -1,46 +1,70 @@ -// TODO: Add unit tests - -// import { Address } from '@spartacus/core'; -// import { Card } from '@spartacus/storefront'; -// import { GetAddressCardContent } from './get-address-card-content.pipe'; - -// const mockedAddress: Address = { -// country: { isocode: 'PL' }, -// titleCode: 'mr', -// firstName: 'John', -// lastName: 'Doe', -// line1: 'Noname street', -// line2: '', -// town: 'Warsaw', -// postalCode: '02651', -// phone: '', -// cellphone: '', -// defaultAddress: false, -// }; - -// describe('GetAddressCardContent', () => { -// const pipe = new GetAddressCardContent(); - -// it('should return empty object if address has not been provided', () => { -// expect(pipe.transform(undefined as unknown as Address)).toEqual({}); -// }); - -// it('should show region as address region iso code if iso code present', () => { -// const isocode: string = 'testIso'; -// const address: Address = { -// ...mockedAddress, -// region: { isocode }, -// }; - -// expect(pipe.transform(address)).toContain(isocode); -// }); - -// it('should transform address object to card object', () => { -// const expectedResult: Card = { -// textBold: 'John Doe', -// text: ['Noname street', '', 'Warsaw, PL', '02651', ''], -// }; - -// expect(pipe.transform(mockedAddress)).toEqual(expectedResult); -// }); -// }); +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { TestBed } from '@angular/core/testing'; +import { Address } from '@spartacus/core'; +import { GetAddressCardContent } from './get-address-card-content.pipe'; + +describe('GetAddressCardContentPipe', () => { + let pipe: GetAddressCardContent; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [GetAddressCardContent], + }); + + pipe = TestBed.inject(GetAddressCardContent); + }); + + it('should create an instance', () => { + expect(pipe).toBeTruthy(); + }); + + it('should transform address to card content', () => { + const address = { + firstName: 'John', + lastName: 'Doe', + line1: '123 Main St', + line2: 'Apt 4B', + town: 'Cityville', + region: { isocode: 'CA' }, + country: { isocode: 'US' }, + postalCode: '12345', + phone: '555-1234', + }; + + const result = pipe.transform(address); + + expect(result).toEqual({ + textBold: 'John Doe', + text: ['123 Main St', 'Apt 4B', 'Cityville, CA, US', '12345', '555-1234'], + }); + }); + + it('should handle missing address', () => { + const result = pipe.transform(null as unknown as Address); + + expect(result).toEqual({}); + }); + + it('should handle missing region and country', () => { + const address = { + firstName: 'Jane', + lastName: 'Smith', + line1: '456 Elm St', + town: 'Townsville', + postalCode: '67890', + phone: '555-5678', + }; + + const result = pipe.transform(address); + console.log(result); + expect(result).toEqual({ + textBold: 'Jane Smith', + text: ['456 Elm St', undefined, 'Townsville', '67890', '555-5678'], + }); + }); +}); diff --git a/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/get-address-card-content.pipe.ts b/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/get-address-card-content.pipe.ts index eff20fc82eb..40980d66cd9 100644 --- a/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/get-address-card-content.pipe.ts +++ b/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/get-address-card-content.pipe.ts @@ -17,21 +17,43 @@ export class GetAddressCardContent implements PipeTransform { return {}; } - let region = ''; - - if (address.region && address.region.isocode) { - region = address.region.isocode + ', '; - } - return { - textBold: address.firstName + ' ' + address.lastName, + textBold: `${address.firstName} ${address.lastName}`, text: [ address.line1, address.line2, - address.town + ', ' + region + address.country?.isocode, + this.getTownLine(address), address.postalCode, address.phone, ], } as Card; } + + protected getTownLine(address: Address): string { + const region = address.region?.isocode || ''; + const town = address.town || ''; + const countryIsocode = address.country?.isocode || ''; + + const townLineParts = []; + + if (town) { + townLineParts.push(town); + } + + if (region) { + if (town) { + townLineParts.push(', '); + } + townLineParts.push(region); + } + + if (countryIsocode) { + if (town || region) { + townLineParts.push(', '); + } + townLineParts.push(countryIsocode); + } + + return townLineParts.join(''); + } } diff --git a/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/opf-checkout-billing-address-form.component.spec.ts b/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/opf-checkout-billing-address-form.component.spec.ts index 14996fb3ced..e023c71832c 100644 --- a/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/opf-checkout-billing-address-form.component.spec.ts +++ b/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/opf-checkout-billing-address-form.component.spec.ts @@ -1,119 +1,156 @@ -// TODO: // Add unit tests - -// import { Component, Input, Pipe, PipeTransform } from '@angular/core'; -// import { ComponentFixture, TestBed } from '@angular/core/testing'; -// import { ReactiveFormsModule } from '@angular/forms'; -// import { -// CheckoutDeliveryAddressFacade, -// CheckoutPaymentFacade, -// } from '@spartacus/checkout/base/root'; -// import { -// Address, -// Country, -// I18nTestingModule, -// UserPaymentService, -// } from '@spartacus/core'; -// import { Card, IconTestingModule } from '@spartacus/storefront'; -// import { of } from 'rxjs'; -// import { OpfCheckoutBillingAddressFormComponent } from './opf-checkout-billing-address-form.component'; -// import createSpy = jasmine.createSpy; - -// const mockAddress = { -// firstName: 'John', -// lastName: 'Doe', -// line1: 'Street 1', -// line2: 'Street 2', -// town: 'Town', -// region: { isocode: 'Region' }, -// country: { isocode: 'Country' }, -// postalCode: 'Postal Code', -// phone: 'Phone', -// }; - -// const mockCountries = [{ isocode: 'Country', name: 'Test' }]; - -// class MockCheckoutDeliveryAddressService -// implements Partial -// { -// getDeliveryAddressState = createSpy().and.returnValue( -// of({ loading: false, error: false, data: mockAddress }) -// ); -// } - -// class MockUserPaymentService implements Partial { -// getAllBillingCountries = createSpy().and.returnValue(of(mockCountries)); -// loadBillingCountries = () => {}; -// } - -// class MockCheckoutPaymentService implements Partial { -// createPaymentDetails = createSpy().and.returnValue(of()); -// } - -// @Pipe({ -// name: 'cxUrl', -// }) -// class MockUrlPipe implements PipeTransform { -// transform() {} -// } - -// @Component({ -// selector: 'cx-address-form', -// template: '', -// }) -// class MockAddressFormComponent { -// @Input() showTitleCode: boolean; -// @Input() setAsDefaultField: boolean; -// @Input() showCancelBtn: boolean; -// @Input() addressData: Address; -// @Input() actionBtnLabel: any; -// @Input() cancelBtnLabel: any; -// @Input() submitAddress: any; -// @Input() backToAddress: any; -// @Input() countries: Country[]; -// } - -// @Component({ -// selector: 'cx-card', -// template: '', -// }) -// class MockCardComponent { -// @Input() -// content: Card; -// } - -// describe('OpfCheckoutBillingAddressFormComponent', () => { -// let component: OpfCheckoutBillingAddressFormComponent; -// let fixture: ComponentFixture; - -// beforeEach(async () => { -// await TestBed.configureTestingModule({ -// imports: [ReactiveFormsModule, I18nTestingModule, IconTestingModule], -// declarations: [ -// OpfCheckoutBillingAddressFormComponent, -// MockUrlPipe, -// MockAddressFormComponent, -// MockCardComponent, -// ], -// providers: [ -// { -// provide: CheckoutDeliveryAddressFacade, -// useClass: MockCheckoutDeliveryAddressService, -// }, -// { provide: UserPaymentService, useClass: MockUserPaymentService }, -// { -// provide: CheckoutPaymentFacade, -// useClass: MockCheckoutPaymentService, -// }, -// ], -// }).compileComponents(); - -// fixture = TestBed.createComponent(OpfCheckoutBillingAddressFormComponent); -// component = fixture.componentInstance; - -// fixture.detectChanges(); -// }); - -// it('should create', () => { -// expect(component).toBeTruthy(); -// }); -// }); +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Pipe, PipeTransform } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { Address, Country } from '@spartacus/core'; +import { BehaviorSubject, EMPTY, Observable, of } from 'rxjs'; +import { OpfCheckoutBillingAddressFormComponent } from './opf-checkout-billing-address-form.component'; +import { OpfCheckoutBillingAddressFormService } from './opf-checkout-billing-address-form.service'; + +class Service { + billingAddress$ = new BehaviorSubject
(undefined); + isLoadingAddress$ = new BehaviorSubject(false); + isSameAsDelivery$ = new BehaviorSubject(true); + + getCountries(): Observable { + return EMPTY; + } + + getAddresses(): void {} + + putDeliveryAddressAsPaymentAddress(): void {} + + setBillingAddress(address: Address): Observable
{ + return of(address); + } + + get billingAddressValue(): Address | undefined { + return this.billingAddress$.value; + } + + get isSameAsDeliveryValue(): boolean { + return this.isSameAsDelivery$.value; + } + + setIsSameAsDeliveryValue(value: boolean): void { + this.isSameAsDelivery$.next(value); + } +} + +@Pipe({ + name: 'cxTranslate', +}) +class MockTranslatePipe implements PipeTransform { + transform(): any {} +} + +describe('OpfCheckoutBillingAddressFormComponent', () => { + let component: OpfCheckoutBillingAddressFormComponent; + let fixture: ComponentFixture; + let service: OpfCheckoutBillingAddressFormService; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [OpfCheckoutBillingAddressFormComponent, MockTranslatePipe], + providers: [ + { + provide: OpfCheckoutBillingAddressFormService, + useClass: Service, + }, + ], + }).compileComponents(); + + service = TestBed.inject(OpfCheckoutBillingAddressFormService); + fixture = TestBed.createComponent(OpfCheckoutBillingAddressFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create the component', () => { + expect(component).toBeTruthy(); + }); + + it('should initialize countries and addresses on ngOnInit', () => { + const countries = [{ id: '1', name: 'Country 1' }]; + spyOn(service, 'getCountries').and.returnValue(of(countries)); + spyOn(service, 'getAddresses'); + + component.ngOnInit(); + + expect(component.countries$).toBeDefined(); + expect(service.getCountries).toHaveBeenCalled(); + expect(service.getAddresses).toHaveBeenCalled(); + }); + + it('should cancel and hide form on cancelAndHideForm', () => { + const setIsSameAsDeliveryValueSpy = spyOn( + service, + 'setIsSameAsDeliveryValue' + ); + component.isEditBillingAddress = true; + component.isAddingBillingAddressInProgress = true; + + component.cancelAndHideForm(); + + expect(component.isEditBillingAddress).toBe(false); + expect(setIsSameAsDeliveryValueSpy).toHaveBeenCalledWith(true); + expect(component.isAddingBillingAddressInProgress).toBe(false); + }); + + it('should set isEditBillingAddress to true on editCustomBillingAddress', () => { + component.editCustomBillingAddress(); + expect(component.isEditBillingAddress).toBe(true); + }); + + it('should toggle same as delivery address on toggleSameAsDeliveryAddress', () => { + const mockEvent = { target: { checked: true } as unknown } as Event; + const putDeliveryAddressAsPaymentAddressSpy = spyOn( + service, + 'putDeliveryAddressAsPaymentAddress' + ); + const setIsSameAsDeliveryValueSpy = spyOn( + service, + 'setIsSameAsDeliveryValue' + ); + component.isAddingBillingAddressInProgress = true; + + component.toggleSameAsDeliveryAddress(mockEvent); + + expect(setIsSameAsDeliveryValueSpy).toHaveBeenCalledWith(true); + expect(putDeliveryAddressAsPaymentAddressSpy).toHaveBeenCalled(); + expect(component.isEditBillingAddress).toBe(false); + }); + + it('should return billingAddress if valid and not adding on getAddressData', () => { + component.isAddingBillingAddressInProgress = false; + const billingAddress = { id: '1', streetName: '123 Main St' }; + + const result = component.getAddressData(billingAddress); + + expect(result).toEqual(billingAddress); + }); + + it('should reset flags and call setBillingAddress on onSubmitAddress', () => { + spyOn(service, 'setBillingAddress').and.returnValue(of()); + const address = { id: '1', streetName: '456 Elm St' }; + + component.onSubmitAddress(address); + + expect(component.isEditBillingAddress).toBe(false); + expect(component.isAddingBillingAddressInProgress).toBe(false); + expect(service.setBillingAddress).toHaveBeenCalledWith(address); + }); + + it('should not call setBillingAddress if address is falsy on onSubmitAddress', () => { + spyOn(service, 'setBillingAddress'); + const address = null as unknown as Address; + + component.onSubmitAddress(address); + + expect(service.setBillingAddress).not.toHaveBeenCalled(); + }); +}); diff --git a/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/opf-checkout-billing-address-form.component.ts b/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/opf-checkout-billing-address-form.component.ts index e1edaef8989..e3dce5b21ce 100644 --- a/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/opf-checkout-billing-address-form.component.ts +++ b/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/opf-checkout-billing-address-form.component.ts @@ -14,7 +14,6 @@ import { OpfCheckoutBillingAddressFormService } from './opf-checkout-billing-add selector: 'cx-opf-checkout-billing-address-form', templateUrl: './opf-checkout-billing-address-form.component.html', changeDetection: ChangeDetectionStrategy.OnPush, - providers: [OpfCheckoutBillingAddressFormService], }) export class OpfCheckoutBillingAddressFormComponent implements OnInit { iconTypes = ICON_TYPE; diff --git a/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/opf-checkout-billing-address-form.module.ts b/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/opf-checkout-billing-address-form.module.ts index 24cad3ba427..2dd7a56f6fa 100644 --- a/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/opf-checkout-billing-address-form.module.ts +++ b/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/opf-checkout-billing-address-form.module.ts @@ -19,6 +19,7 @@ import { } from '@spartacus/storefront'; import { GetAddressCardContent } from './get-address-card-content.pipe'; import { OpfCheckoutBillingAddressFormComponent } from './opf-checkout-billing-address-form.component'; +import { OpfCheckoutBillingAddressFormService } from './opf-checkout-billing-address-form.service'; @NgModule({ declarations: [OpfCheckoutBillingAddressFormComponent, GetAddressCardContent], @@ -35,5 +36,6 @@ import { OpfCheckoutBillingAddressFormComponent } from './opf-checkout-billing-a AddressFormModule, SpinnerModule, ], + providers: [OpfCheckoutBillingAddressFormService], }) export class OpfCheckoutBillingAddressFormModule {} diff --git a/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/opf-checkout-billing-address-form.service.spec.ts b/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/opf-checkout-billing-address-form.service.spec.ts new file mode 100644 index 00000000000..f7bff6dcc7b --- /dev/null +++ b/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/opf-checkout-billing-address-form.service.spec.ts @@ -0,0 +1,196 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { fakeAsync, flush, TestBed } from '@angular/core/testing'; +import { ActiveCartFacade, Cart } from '@spartacus/cart/base/root'; +import { + CheckoutBillingAddressFacade, + CheckoutDeliveryAddressFacade, +} from '@spartacus/checkout/base/root'; +import { + Address, + GlobalMessageService, + UserPaymentService, +} from '@spartacus/core'; +import { of, throwError } from 'rxjs'; +import { OpfCheckoutPaymentWrapperService } from '../opf-checkout-payment-wrapper'; +import { OpfCheckoutBillingAddressFormService } from './opf-checkout-billing-address-form.service'; + +describe('OpfCheckoutBillingAddressFormService', () => { + let service: OpfCheckoutBillingAddressFormService; + let mockDeliveryAddressFacade: Partial; + let mockBillingAddressFacade: Partial; + let mockUserPaymentService: Partial; + let mockActiveCartFacade: Partial; + let mockGlobalMessageService: Partial; + let mockOpfCheckoutPaymentWrapperService: Partial; + + const mockDeliveryAddress: Address = { + id: '123', + }; + const mockPaymentAddress: Address = { + id: '321', + }; + + beforeEach(() => { + mockDeliveryAddressFacade = { + getDeliveryAddressState: () => + of({ loading: false, data: mockDeliveryAddress, error: false }), + }; + + mockBillingAddressFacade = { + setBillingAddress: (address: Address) => of(address), + }; + + mockUserPaymentService = { + getAllBillingCountries: () => of([]), + loadBillingCountries: () => {}, + }; + + mockActiveCartFacade = { + reloadActiveCart: () => of(true), + isStable: () => of(true), + getActive: () => of({ paymentAddress: mockPaymentAddress } as Cart), + }; + + mockGlobalMessageService = { + add: () => {}, + }; + + mockOpfCheckoutPaymentWrapperService = { + reloadPaymentMode: () => {}, + }; + + TestBed.configureTestingModule({ + providers: [ + OpfCheckoutBillingAddressFormService, + { + provide: CheckoutDeliveryAddressFacade, + useValue: mockDeliveryAddressFacade, + }, + { + provide: CheckoutBillingAddressFacade, + useValue: mockBillingAddressFacade, + }, + { provide: UserPaymentService, useValue: mockUserPaymentService }, + { provide: ActiveCartFacade, useValue: mockActiveCartFacade }, + { provide: GlobalMessageService, useValue: mockGlobalMessageService }, + { + provide: OpfCheckoutPaymentWrapperService, + useValue: mockOpfCheckoutPaymentWrapperService, + }, + ], + }); + + service = TestBed.inject(OpfCheckoutBillingAddressFormService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should load countries', () => { + spyOn(mockUserPaymentService, 'loadBillingCountries'); + + service.getCountries().subscribe(() => { + expect(mockUserPaymentService.loadBillingCountries).toHaveBeenCalled(); + }); + }); + + it('should get addresses when only payment address is present', () => { + spyOn(mockBillingAddressFacade, 'setBillingAddress').and.returnValue( + of(true) + ); + spyOn(mockActiveCartFacade, 'isStable').and.returnValue(of(true)); + + service.getAddresses(); + + expect(service['isLoadingAddressSub'].value).toBeFalsy(); + expect(service.billingAddressValue).toEqual(mockPaymentAddress); + expect(service.isSameAsDeliveryValue).toBeFalsy(); + }); + + it('should put delivery address as payment address', () => { + spyOn(mockDeliveryAddressFacade, 'getDeliveryAddressState').and.returnValue( + of({ loading: false, data: mockDeliveryAddress, error: false }) + ); + spyOn(mockBillingAddressFacade, 'setBillingAddress').and.returnValue( + of(true) + ); + + service.putDeliveryAddressAsPaymentAddress(); + + expect(service.isSameAsDeliveryValue).toBeTruthy(); + }); + + it('should put delivery address as payment address and handle error', () => { + spyOn(mockDeliveryAddressFacade, 'getDeliveryAddressState').and.returnValue( + of({ loading: false, data: mockDeliveryAddress, error: false }) + ); + spyOn(mockBillingAddressFacade, 'setBillingAddress').and.returnValue( + throwError({}) + ); + + service.putDeliveryAddressAsPaymentAddress(); + + expect(service.isSameAsDeliveryValue).toBeFalsy(); + }); + + it('should get delivery address', (done) => { + spyOn(mockDeliveryAddressFacade, 'getDeliveryAddressState').and.returnValue( + of({ loading: false, data: mockDeliveryAddress, error: false }) + ); + + service['getDeliveryAddress']().subscribe((result) => { + expect(result).toEqual(mockDeliveryAddress); + done(); + }); + }); + + it('should not get delivery address when loading', fakeAsync(() => { + spyOn(mockDeliveryAddressFacade, 'getDeliveryAddressState').and.returnValue( + of({ loading: true, data: undefined, error: false }) + ); + + let address; + + service['getDeliveryAddress']().subscribe((result) => { + address = result; + flush(); + }); + + expect(address).toBeUndefined(); + })); + + it('should get payment address', () => { + spyOn(mockActiveCartFacade, 'getActive').and.returnValue( + of({ paymentAddress: mockPaymentAddress } as Cart) + ); + + service['getPaymentAddress']().subscribe((result) => { + expect(result).toEqual(mockPaymentAddress); + }); + }); + + it('should not get payment address when not present', () => { + spyOn(mockActiveCartFacade, 'getActive').and.returnValue( + of({ paymentAddress: undefined } as Cart) + ); + + service['getPaymentAddress']().subscribe((result) => { + expect(result).toBeUndefined(); + }); + }); + + it('should set isSameAsDelivery value', () => { + const newValue = false; + spyOn(service['isSameAsDeliverySub'], 'next'); + + service.setIsSameAsDeliveryValue(newValue); + + expect(service['isSameAsDeliverySub'].next).toHaveBeenCalledWith(newValue); + }); +});