diff --git a/docs/libs/creating-lib.md b/docs/libs/creating-lib.md index de95d5edb93..74248b7feba 100644 --- a/docs/libs/creating-lib.md +++ b/docs/libs/creating-lib.md @@ -60,9 +60,8 @@ Just copy paste the following and and make sure to rename `TODO:` to you lib's n module.exports = function (config) { config.set({ basePath: '', - frameworks: ['parallel', 'jasmine', '@angular-devkit/build-angular'], + frameworks: ['jasmine', '@angular-devkit/build-angular'], plugins: [ - require('karma-parallel'), require('karma-jasmine'), require('karma-coverage'), require('karma-chrome-launcher'), @@ -70,10 +69,6 @@ module.exports = function (config) { require('@angular-devkit/build-angular/plugins/karma'), require('karma-junit-reporter'), ], - parallelOptions: { - executors: 2, - shardStrategy: 'round-robin', - }, client: { clearContext: false, // leave Jasmine Spec Runner output visible in browser }, @@ -220,7 +215,7 @@ Use the following template: "peerDependencies": { "@angular/common": "^10.1.0", "@angular/core": "^10.1.0", - "rxjs": "^6.6.0", + "rxjs": "^7.8.0", "@spartacus/core": "3.0.0-next.0", "@spartacus/storefront": "3.0.0-next.0" } diff --git a/feature-libs/asm/components/asm-bind-cart/asm-bind-cart.component.spec.ts b/feature-libs/asm/components/asm-bind-cart/asm-bind-cart.component.spec.ts index b7d5788ceae..132fa7e8d48 100644 --- a/feature-libs/asm/components/asm-bind-cart/asm-bind-cart.component.spec.ts +++ b/feature-libs/asm/components/asm-bind-cart/asm-bind-cart.component.spec.ts @@ -299,7 +299,9 @@ describe('AsmBindCartComponent', () => { it('should alert through global messsages when the bind cart fails', () => { const expectedErrorMessage = 'mock-error-message'; (asmBindCartFacade.bindCart as jasmine.Spy).and.returnValue( - throwError({ details: [{ message: expectedErrorMessage }] }) + throwError(() => ({ + details: [{ message: expectedErrorMessage }], + })) ); component.bindCartToCustomer(); diff --git a/feature-libs/asm/karma.conf.js b/feature-libs/asm/karma.conf.js index 3fe7e8cffb4..ee7d7ce422b 100644 --- a/feature-libs/asm/karma.conf.js +++ b/feature-libs/asm/karma.conf.js @@ -4,9 +4,8 @@ module.exports = function (config) { config.set({ basePath: '', - frameworks: ['parallel', 'jasmine', '@angular-devkit/build-angular'], + frameworks: ['jasmine', '@angular-devkit/build-angular'], plugins: [ - require('karma-parallel'), require('karma-jasmine'), require('karma-coverage'), require('karma-chrome-launcher'), @@ -14,10 +13,6 @@ module.exports = function (config) { require('@angular-devkit/build-angular/plugins/karma'), require('karma-junit-reporter'), ], - parallelOptions: { - executors: 2, - shardStrategy: 'round-robin', - }, client: { clearContext: false, // leave Jasmine Spec Runner output visible in browser jasmine: { diff --git a/feature-libs/asm/occ/adapters/occ-asm.adapter.ts b/feature-libs/asm/occ/adapters/occ-asm.adapter.ts index 7f2298da57a..12ba3a03814 100644 --- a/feature-libs/asm/occ/adapters/occ-asm.adapter.ts +++ b/feature-libs/asm/occ/adapters/occ-asm.adapter.ts @@ -29,7 +29,7 @@ import { User, normalizeHttpError, } from '@spartacus/core'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; @Injectable() @@ -75,7 +75,9 @@ export class OccAsmAdapter implements AsmAdapter { ); return this.http.get(url, { headers, params }).pipe( - catchError((error) => throwError(normalizeHttpError(error, this.logger))), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), this.converterService.pipeable(CUSTOMER_LISTS_NORMALIZER) ); } @@ -123,7 +125,9 @@ export class OccAsmAdapter implements AsmAdapter { ); return this.http.get(url, { headers, params }).pipe( - catchError((error) => throwError(normalizeHttpError(error, this.logger))), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), this.converterService.pipeable(CUSTOMER_SEARCH_PAGE_NORMALIZER) ); } @@ -148,13 +152,11 @@ export class OccAsmAdapter implements AsmAdapter { } ); - return this.http - .post(url, {}, { headers, params }) - .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ) - ); + return this.http.post(url, {}, { headers, params }).pipe( + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }) + ); } createCustomer(user: CustomerRegistrationForm): Observable { @@ -172,12 +174,10 @@ export class OccAsmAdapter implements AsmAdapter { prefix: false, } ); - return this.http - .post(url, user, { headers, params }) - .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ) - ); + return this.http.post(url, user, { headers, params }).pipe( + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }) + ); } } diff --git a/feature-libs/asm/package.json b/feature-libs/asm/package.json index f566dee9a43..1270b6aed57 100644 --- a/feature-libs/asm/package.json +++ b/feature-libs/asm/package.json @@ -41,7 +41,7 @@ "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", "@spartacus/user": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/feature-libs/asm/root/services/asm-auth.service.ts b/feature-libs/asm/root/services/asm-auth.service.ts index b295b8c7438..92cc950ca1d 100644 --- a/feature-libs/asm/root/services/asm-auth.service.ts +++ b/feature-libs/asm/root/services/asm-auth.service.ts @@ -19,7 +19,7 @@ import { StateWithClientAuth, UserIdService, } from '@spartacus/core'; -import { combineLatest, from, Observable, of } from 'rxjs'; +import { combineLatest, from, lastValueFrom, Observable, of } from 'rxjs'; import { map, switchMap, take } from 'rxjs/operators'; import { AsmAuthStorageService, TokenTarget } from './asm-auth-storage.service'; @@ -109,9 +109,8 @@ export class AsmAuthService extends AuthService { * To perform logout it is best to use `logout` method. Use this method with caution. */ coreLogout(): Promise { - return this.userIdService - .isEmulated() - .pipe( + return lastValueFrom( + this.userIdService.isEmulated().pipe( take(1), switchMap((isEmulated) => { if (isEmulated) { @@ -124,7 +123,7 @@ export class AsmAuthService extends AuthService { } }) ) - .toPromise(); + ); } /** diff --git a/feature-libs/cart/base/core/cart-persistence.module.ts b/feature-libs/cart/base/core/cart-persistence.module.ts index 63009cf333f..0ca9c34ba9e 100644 --- a/feature-libs/cart/base/core/cart-persistence.module.ts +++ b/feature-libs/cart/base/core/cart-persistence.module.ts @@ -5,13 +5,14 @@ */ import { NgModule } from '@angular/core'; -import { Action, ActionReducer, MetaReducer, META_REDUCERS } from '@ngrx/store'; +import { Action, ActionReducer, META_REDUCERS, MetaReducer } from '@ngrx/store'; import { CartType } from '@spartacus/cart/base/root'; import { Config, ConfigInitializerService, MODULE_INITIALIZER, } from '@spartacus/core'; +import { lastValueFrom } from 'rxjs'; import { tap } from 'rxjs/operators'; import { MultiCartStatePersistenceService } from './services/multi-cart-state-persistence.service'; @@ -19,16 +20,14 @@ export function cartStatePersistenceFactory( cartStatePersistenceService: MultiCartStatePersistenceService, configInit: ConfigInitializerService ): () => Promise { - const result = () => - configInit - .getStable('context') - .pipe( + return () => + lastValueFrom( + configInit.getStable('context').pipe( tap(() => { cartStatePersistenceService.initSync(); }) ) - .toPromise(); - return result; + ); } /** diff --git a/feature-libs/cart/base/core/facade/multi-cart.service.ts b/feature-libs/cart/base/core/facade/multi-cart.service.ts index 4feccdbb5a3..e4f2eeca998 100644 --- a/feature-libs/cart/base/core/facade/multi-cart.service.ts +++ b/feature-libs/cart/base/core/facade/multi-cart.service.ts @@ -13,7 +13,7 @@ import { OrderEntry, } from '@spartacus/cart/base/root'; import { isNotUndefined, StateUtils, UserIdService } from '@spartacus/core'; -import { EMPTY, Observable, timer } from 'rxjs'; +import { Observable, of, timer } from 'rxjs'; import { debounce, distinctUntilChanged, @@ -76,7 +76,7 @@ export class MultiCartService implements MultiCartFacade { // This flickering should only be avoided when switching from false to true // Start of loading should be showed instantly (no debounce) // Extra actions are only dispatched after some loading - debounce((isStable) => (isStable ? timer(0) : EMPTY)), + debounce((isStable) => (isStable ? timer(0) : of(undefined))), distinctUntilChanged() ); } diff --git a/feature-libs/cart/base/core/store/effects/cart-voucher.effect.spec.ts b/feature-libs/cart/base/core/store/effects/cart-voucher.effect.spec.ts index e9ef0c4a169..0c9cafa2e35 100644 --- a/feature-libs/cart/base/core/store/effects/cart-voucher.effect.spec.ts +++ b/feature-libs/cart/base/core/store/effects/cart-voucher.effect.spec.ts @@ -82,7 +82,9 @@ describe('Cart Voucher effect', () => { it('should fail', () => { const error = new HttpErrorResponse({ error: 'error' }); - cartVoucherConnector.add = createSpy().and.returnValue(throwError(error)); + cartVoucherConnector.add = createSpy().and.returnValue( + throwError(() => error) + ); const action = new CartActions.CartAddVoucher({ userId, cartId, diff --git a/feature-libs/cart/base/core/store/effects/cart.effect.spec.ts b/feature-libs/cart/base/core/store/effects/cart.effect.spec.ts index d542acb507c..858fb83db73 100644 --- a/feature-libs/cart/base/core/store/effects/cart.effect.spec.ts +++ b/feature-libs/cart/base/core/store/effects/cart.effect.spec.ts @@ -165,13 +165,13 @@ describe('Cart effect', () => { extraData: { active: true }, }); loadMock.and.returnValue( - throwError({ + throwError(() => ({ error: { errors: [ { reason: 'notFound', subjectType: 'cart', subject: '123456' }, ], }, - }) + })) ); const removeCartCompletion = new CartActions.RemoveCart({ cartId }); actions$ = hot('-a', { a: action }); @@ -199,7 +199,7 @@ describe('Cart effect', () => { }, }); const action = new CartActions.LoadCart(payload); - loadMock.and.returnValue(throwError(httpError)); + loadMock.and.returnValue(throwError(() => httpError)); const removeCartCompletion = new CartActions.LoadCartFail({ ...payload, error: normalizeHttpError(httpError), diff --git a/feature-libs/cart/base/occ/adapters/occ-cart-validation.adapter.ts b/feature-libs/cart/base/occ/adapters/occ-cart-validation.adapter.ts index 67b03cfa82c..72bbcf86878 100644 --- a/feature-libs/cart/base/occ/adapters/occ-cart-validation.adapter.ts +++ b/feature-libs/cart/base/occ/adapters/occ-cart-validation.adapter.ts @@ -17,7 +17,7 @@ import { OccEndpointsService, normalizeHttpError, } from '@spartacus/core'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; @Injectable() @@ -36,7 +36,9 @@ export class OccCartValidationAdapter implements CartValidationAdapter { }); return this.http.post(url, null).pipe( - catchError((error) => throwError(normalizeHttpError(error, this.logger))), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), this.converter.pipeable(CART_VALIDATION_NORMALIZER) ); } diff --git a/feature-libs/cart/base/occ/adapters/occ-cart-voucher.adapter.ts b/feature-libs/cart/base/occ/adapters/occ-cart-voucher.adapter.ts index 0b7b4bddafb..89a0e32015e 100644 --- a/feature-libs/cart/base/occ/adapters/occ-cart-voucher.adapter.ts +++ b/feature-libs/cart/base/occ/adapters/occ-cart-voucher.adapter.ts @@ -5,21 +5,25 @@ */ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { CartVoucherAdapter } from '@spartacus/cart/base/core'; import { CART_VOUCHER_NORMALIZER } from '@spartacus/cart/base/root'; import { ConverterService, InterceptorUtil, - OccEndpointsService, + LoggerService, OCC_USER_ID_ANONYMOUS, + OccEndpointsService, USE_CLIENT_TOKEN, + normalizeHttpError, } from '@spartacus/core'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; @Injectable() export class OccCartVoucherAdapter implements CartVoucherAdapter { + protected logger = inject(LoggerService); + constructor( protected http: HttpClient, protected occEndpoints: OccEndpointsService, @@ -54,7 +58,9 @@ export class OccCartVoucherAdapter implements CartVoucherAdapter { const headers = this.getHeaders(userId); return this.http.post(url, toAdd, { headers, params }).pipe( - catchError((error: any) => throwError(error)), + catchError((error: any) => { + throw normalizeHttpError(error, this.logger); + }), this.converter.pipeable(CART_VOUCHER_NORMALIZER) ); } @@ -67,8 +73,10 @@ export class OccCartVoucherAdapter implements CartVoucherAdapter { const headers = this.getHeaders(userId); - return this.http - .delete(url, { headers }) - .pipe(catchError((error: any) => throwError(error))); + return this.http.delete(url, { headers }).pipe( + catchError((error: any) => { + throw normalizeHttpError(error, this.logger); + }) + ); } } diff --git a/feature-libs/cart/package.json b/feature-libs/cart/package.json index 811607fa691..72788bbe76f 100644 --- a/feature-libs/cart/package.json +++ b/feature-libs/cart/package.json @@ -43,7 +43,7 @@ "@spartacus/styles": "6.6.0-1", "@spartacus/user": "6.6.0-1", "bootstrap": "^4.6.2", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/feature-libs/cart/quick-order/components/page-context/quick-order-order-entries.context.spec.ts b/feature-libs/cart/quick-order/components/page-context/quick-order-order-entries.context.spec.ts index 3d2e4642e78..220c166c744 100644 --- a/feature-libs/cart/quick-order/components/page-context/quick-order-order-entries.context.spec.ts +++ b/feature-libs/cart/quick-order/components/page-context/quick-order-order-entries.context.spec.ts @@ -204,11 +204,11 @@ describe('QuickOrderOrderEntriesContext', () => { it('should catch unknown identifier error', () => { canAdd$.next(true); productConnector.get = createSpy().and.returnValue( - throwError({ + throwError(() => ({ error: { errors: [{ type: 'UnknownIdentifierError' }], }, - }) + })) ); const unableToAddProductsData: ProductData[] = [ @@ -240,7 +240,9 @@ describe('QuickOrderOrderEntriesContext', () => { it('should catch unknown errors', () => { canAdd$.next(true); - productConnector.get = createSpy().and.returnValue(throwError({})); + productConnector.get = createSpy().and.returnValue( + throwError(() => ({})) + ); const unableToAddProductsData: ProductData[] = [ { productCode: unhandledItemErrorId, quantity: 1 }, diff --git a/feature-libs/checkout/b2b/occ/adapters/occ-checkout-cost-center.adapter.spec.ts b/feature-libs/checkout/b2b/occ/adapters/occ-checkout-cost-center.adapter.spec.ts index 5b5a590293e..a67f98a0778 100644 --- a/feature-libs/checkout/b2b/occ/adapters/occ-checkout-cost-center.adapter.spec.ts +++ b/feature-libs/checkout/b2b/occ/adapters/occ-checkout-cost-center.adapter.spec.ts @@ -103,7 +103,9 @@ describe(`OccCheckoutCostCenterAdapter`, () => { describe(`back-off`, () => { it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { - spyOn(httpClient, 'put').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'put').and.returnValue( + throwError(() => mockJaloError) + ); let result: HttpErrorModel | undefined; const subscription = service @@ -127,7 +129,7 @@ describe(`OccCheckoutCostCenterAdapter`, () => { if (calledTimes === 3) { return of(cartData); } - return throwError(mockJaloError); + return throwError(() => mockJaloError); }) ); diff --git a/feature-libs/checkout/b2b/occ/adapters/occ-checkout-cost-center.adapter.ts b/feature-libs/checkout/b2b/occ/adapters/occ-checkout-cost-center.adapter.ts index b7223863530..b7b31dcfee6 100644 --- a/feature-libs/checkout/b2b/occ/adapters/occ-checkout-cost-center.adapter.ts +++ b/feature-libs/checkout/b2b/occ/adapters/occ-checkout-cost-center.adapter.ts @@ -16,7 +16,7 @@ import { normalizeHttpError, OccEndpointsService, } from '@spartacus/core'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; @Injectable() @@ -37,9 +37,9 @@ export class OccCheckoutCostCenterAdapter implements CheckoutCostCenterAdapter { return this.http .put(this.getSetCartCostCenterEndpoint(userId, cartId, costCenterId), {}) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), backOff({ shouldRetry: isJaloError }), this.converter.pipeable(CART_NORMALIZER) ); diff --git a/feature-libs/checkout/b2b/occ/adapters/occ-checkout-payment-type.adapter.spec.ts b/feature-libs/checkout/b2b/occ/adapters/occ-checkout-payment-type.adapter.spec.ts index 1abcf14f84b..74758ce764e 100644 --- a/feature-libs/checkout/b2b/occ/adapters/occ-checkout-payment-type.adapter.spec.ts +++ b/feature-libs/checkout/b2b/occ/adapters/occ-checkout-payment-type.adapter.spec.ts @@ -130,7 +130,9 @@ describe(`OccCheckoutPaymentTypeAdapter`, () => { describe(`back-off`, () => { it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { - spyOn(httpClient, 'get').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'get').and.returnValue( + throwError(() => mockJaloError) + ); let result: HttpErrorModel | undefined; const subscription = service @@ -154,7 +156,7 @@ describe(`OccCheckoutPaymentTypeAdapter`, () => { if (calledTimes === 3) { return of(paymentTypesList); } - return throwError(mockJaloError); + return throwError(() => mockJaloError); }) ); @@ -233,7 +235,9 @@ describe(`OccCheckoutPaymentTypeAdapter`, () => { describe(`back-off`, () => { it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { - spyOn(httpClient, 'put').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'put').and.returnValue( + throwError(() => mockJaloError) + ); let result: HttpErrorModel | undefined; const subscription = service @@ -257,7 +261,7 @@ describe(`OccCheckoutPaymentTypeAdapter`, () => { if (calledTimes === 3) { return of(cartData); } - return throwError(mockJaloError); + return throwError(() => mockJaloError); }) ); diff --git a/feature-libs/checkout/b2b/occ/adapters/occ-checkout-payment-type.adapter.ts b/feature-libs/checkout/b2b/occ/adapters/occ-checkout-payment-type.adapter.ts index 204b7dbb7f4..469ff9d015b 100644 --- a/feature-libs/checkout/b2b/occ/adapters/occ-checkout-payment-type.adapter.ts +++ b/feature-libs/checkout/b2b/occ/adapters/occ-checkout-payment-type.adapter.ts @@ -21,7 +21,7 @@ import { isJaloError, normalizeHttpError, } from '@spartacus/core'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; @Injectable() @@ -44,9 +44,9 @@ export class OccCheckoutPaymentTypeAdapter return this.http .get(this.getPaymentTypesEndpoint(), { context }) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), backOff({ shouldRetry: isJaloError }), map((paymentTypeList) => paymentTypeList.paymentTypes ?? []), this.converter.pipeableMany(CHECKOUT_PAYMENT_TYPE_NORMALIZER) @@ -74,9 +74,9 @@ export class OccCheckoutPaymentTypeAdapter {} ) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), backOff({ shouldRetry: isJaloError }), this.converter.pipeable(CART_NORMALIZER) ); diff --git a/feature-libs/checkout/base/components/checkout-place-order/checkout-place-order.component.spec.ts b/feature-libs/checkout/base/components/checkout-place-order/checkout-place-order.component.spec.ts index 7d07f2b0c45..7a333cdc55a 100644 --- a/feature-libs/checkout/base/components/checkout-place-order/checkout-place-order.component.spec.ts +++ b/feature-libs/checkout/base/components/checkout-place-order/checkout-place-order.component.spec.ts @@ -24,7 +24,7 @@ class MockOrderFacade implements Partial { } class MockRoutingService implements Partial { - go = createSpy().and.returnValue(of(true).toPromise()); + go = createSpy().and.returnValue(Promise.resolve(true)); } class MockLaunchDialogService implements Partial { diff --git a/feature-libs/checkout/base/components/services/express-checkout.service.spec.ts b/feature-libs/checkout/base/components/services/express-checkout.service.spec.ts index 7e81ce8d1b0..e3016bdf54f 100644 --- a/feature-libs/checkout/base/components/services/express-checkout.service.spec.ts +++ b/feature-libs/checkout/base/components/services/express-checkout.service.spec.ts @@ -213,7 +213,7 @@ describe('ExpressCheckoutService', () => { it('should return false if set delivery address error', (done) => { checkoutDeliveryAddressFacade.setDeliveryAddress = - createSpy().and.returnValue(throwError('err')); + createSpy().and.returnValue(throwError(() => 'err')); service .trySetDefaultCheckoutDetails() @@ -269,7 +269,7 @@ describe('ExpressCheckoutService', () => { it('should return false if set payment method error', (done) => { checkoutPaymentService.setPaymentDetails = createSpy().and.returnValue( - throwError('err') + throwError(() => 'err') ); service @@ -310,7 +310,7 @@ describe('ExpressCheckoutService', () => { it('should return false if set delivery mode error', (done) => { checkoutDeliveryModesFacade.setDeliveryMode = - createSpy().and.returnValue(throwError('err')); + createSpy().and.returnValue(throwError(() => 'err')); service .trySetDefaultCheckoutDetails() diff --git a/feature-libs/checkout/base/core/facade/checkout-delivery-address.service.spec.ts b/feature-libs/checkout/base/core/facade/checkout-delivery-address.service.spec.ts index 6ad8ee50bea..48427effd57 100644 --- a/feature-libs/checkout/base/core/facade/checkout-delivery-address.service.spec.ts +++ b/feature-libs/checkout/base/core/facade/checkout-delivery-address.service.spec.ts @@ -14,7 +14,7 @@ import { QueryState, UserIdService, } from '@spartacus/core'; -import { of } from 'rxjs'; +import { config, of } from 'rxjs'; import { take } from 'rxjs/operators'; import { CheckoutDeliveryAddressConnector } from '../connectors/checkout-delivery-address/checkout-delivery-address.connector'; import { CheckoutDeliveryAddressService } from './checkout-delivery-address.service'; @@ -61,6 +61,22 @@ describe(`CheckoutDeliveryAddressService`, () => { let checkoutQuery: CheckoutQueryFacade; let eventService: EventService; + // TODO: CXSPA-4870 verify if can be avoided + let originalOnUnhandledError: ((err: any) => void) | null; + + beforeAll(() => { + // configure rxjs to not crash node instance with thrown errors + // TODO: CXSPA-4870 verify if can be avoided + originalOnUnhandledError = config.onUnhandledError; + config.onUnhandledError = () => {}; + }); + + afterAll(() => { + // reset rxjs configuration + // TODO: CXSPA-4870 verify if can be avoided + config.onUnhandledError = originalOnUnhandledError; + }); + beforeEach(() => { TestBed.configureTestingModule({ providers: [ diff --git a/feature-libs/checkout/base/core/facade/checkout-delivery-modes.service.spec.ts b/feature-libs/checkout/base/core/facade/checkout-delivery-modes.service.spec.ts index 538115a85d4..16474fba0b8 100644 --- a/feature-libs/checkout/base/core/facade/checkout-delivery-modes.service.spec.ts +++ b/feature-libs/checkout/base/core/facade/checkout-delivery-modes.service.spec.ts @@ -234,7 +234,7 @@ describe(`CheckoutDeliveryModesService`, () => { it(`should dispatch CheckoutDeliveryModeClearedErrorEvent event on error`, () => { connector.clearCheckoutDeliveryMode = createSpy().and.returnValue( - throwError('err') + throwError(() => 'err') ); service.clearCheckoutDeliveryMode(); diff --git a/feature-libs/checkout/base/core/facade/checkout-delivery-modes.service.ts b/feature-libs/checkout/base/core/facade/checkout-delivery-modes.service.ts index bc9354ff575..d866cb1be4b 100644 --- a/feature-libs/checkout/base/core/facade/checkout-delivery-modes.service.ts +++ b/feature-libs/checkout/base/core/facade/checkout-delivery-modes.service.ts @@ -27,8 +27,8 @@ import { QueryState, UserIdService, } from '@spartacus/core'; -import { combineLatest, Observable, throwError } from 'rxjs'; -import { catchError, filter, map, switchMap, take, tap } from 'rxjs/operators'; +import { Observable, combineLatest } from 'rxjs'; +import { filter, map, switchMap, take, tap } from 'rxjs/operators'; import { CheckoutDeliveryModesConnector } from '../connectors/checkout-delivery-modes/checkout-delivery-modes.connector'; @Injectable() @@ -95,35 +95,35 @@ export class CheckoutDeliveryModesService this.checkoutDeliveryModesConnector .clearCheckoutDeliveryMode(userId, cartId) .pipe( - tap(() => { - this.eventService.dispatch( - { - userId, - cartId, - /** - * As we know the cart is not anonymous (precondition checked), - * we can safely use the cartId, which is actually the cart.code. - */ - cartCode: cartId, - }, - CheckoutDeliveryModeClearedEvent - ); - }), - catchError((error) => { - this.eventService.dispatch( - { - userId, - cartId, - /** - * As we know the cart is not anonymous (precondition checked), - * we can safely use the cartId, which is actually the cart.code. - */ - cartCode: cartId, - }, - CheckoutDeliveryModeClearedErrorEvent - ); - - return throwError(error); + tap({ + next: () => { + this.eventService.dispatch( + { + userId, + cartId, + /** + * As we know the cart is not anonymous (precondition checked), + * we can safely use the cartId, which is actually the cart.code. + */ + cartCode: cartId, + }, + CheckoutDeliveryModeClearedEvent + ); + }, + error: () => { + this.eventService.dispatch( + { + userId, + cartId, + /** + * As we know the cart is not anonymous (precondition checked), + * we can safely use the cartId, which is actually the cart.code. + */ + cartCode: cartId, + }, + CheckoutDeliveryModeClearedErrorEvent + ); + }, }) ) ) diff --git a/feature-libs/checkout/base/occ/adapters/occ-checkout-delivery-address.adapter.spec.ts b/feature-libs/checkout/base/occ/adapters/occ-checkout-delivery-address.adapter.spec.ts index c595598f5ee..e88fea58325 100644 --- a/feature-libs/checkout/base/occ/adapters/occ-checkout-delivery-address.adapter.spec.ts +++ b/feature-libs/checkout/base/occ/adapters/occ-checkout-delivery-address.adapter.spec.ts @@ -127,7 +127,9 @@ describe(`OccCheckoutDeliveryAddressAdapter`, () => { describe(`back-off`, () => { it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { - spyOn(httpClient, 'post').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'post').and.returnValue( + throwError(() => mockJaloError) + ); let result: HttpErrorModel | undefined; const subscription = service @@ -151,7 +153,7 @@ describe(`OccCheckoutDeliveryAddressAdapter`, () => { if (calledTimes === 3) { return of(mockAddress); } - return throwError(mockJaloError); + return throwError(() => mockJaloError); }) ); @@ -205,7 +207,9 @@ describe(`OccCheckoutDeliveryAddressAdapter`, () => { describe(`back-off`, () => { it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { - spyOn(httpClient, 'put').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'put').and.returnValue( + throwError(() => mockJaloError) + ); let result: HttpErrorModel | undefined; const subscription = service @@ -229,7 +233,7 @@ describe(`OccCheckoutDeliveryAddressAdapter`, () => { if (calledTimes === 3) { return of(cartData); } - return throwError(mockJaloError); + return throwError(() => mockJaloError); }) ); @@ -283,7 +287,9 @@ describe(`OccCheckoutDeliveryAddressAdapter`, () => { describe(`back-off`, () => { it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { - spyOn(httpClient, 'delete').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'delete').and.returnValue( + throwError(() => mockJaloError) + ); let result: HttpErrorModel | undefined; const subscription = service @@ -307,7 +313,7 @@ describe(`OccCheckoutDeliveryAddressAdapter`, () => { if (calledTimes === 3) { return of(checkoutData); } - return throwError(mockJaloError); + return throwError(() => mockJaloError); }) ); diff --git a/feature-libs/checkout/base/occ/adapters/occ-checkout-delivery-address.adapter.ts b/feature-libs/checkout/base/occ/adapters/occ-checkout-delivery-address.adapter.ts index 3c1ceffd497..62de7f60fd2 100644 --- a/feature-libs/checkout/base/occ/adapters/occ-checkout-delivery-address.adapter.ts +++ b/feature-libs/checkout/base/occ/adapters/occ-checkout-delivery-address.adapter.ts @@ -19,7 +19,7 @@ import { isJaloError, normalizeHttpError, } from '@spartacus/core'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; @Injectable() @@ -50,9 +50,9 @@ export class OccCheckoutDeliveryAddressAdapter } ) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), backOff({ shouldRetry: isJaloError, }), @@ -83,9 +83,9 @@ export class OccCheckoutDeliveryAddressAdapter {} ) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), backOff({ shouldRetry: isJaloError, }) @@ -110,9 +110,9 @@ export class OccCheckoutDeliveryAddressAdapter return this.http .delete(this.getRemoveDeliveryAddressEndpoint(userId, cartId)) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), backOff({ shouldRetry: isJaloError, }) diff --git a/feature-libs/checkout/base/occ/adapters/occ-checkout-delivery-modes.adapter.spec.ts b/feature-libs/checkout/base/occ/adapters/occ-checkout-delivery-modes.adapter.spec.ts index 7b5ec9c0e35..41ec0fc64cc 100644 --- a/feature-libs/checkout/base/occ/adapters/occ-checkout-delivery-modes.adapter.spec.ts +++ b/feature-libs/checkout/base/occ/adapters/occ-checkout-delivery-modes.adapter.spec.ts @@ -121,7 +121,9 @@ describe(`OccCheckoutDeliveryModesAdapter`, () => { describe(`back-off`, () => { it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { - spyOn(httpClient, 'get').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'get').and.returnValue( + throwError(() => mockJaloError) + ); let result: HttpErrorModel | undefined; const subscription = service @@ -144,7 +146,7 @@ describe(`OccCheckoutDeliveryModesAdapter`, () => { if (calledTimes === 3) { return of(mockDeliveryModes); } - return throwError(mockJaloError); + return throwError(() => mockJaloError); }) ); @@ -200,7 +202,9 @@ describe(`OccCheckoutDeliveryModesAdapter`, () => { describe(`back-off`, () => { it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { - spyOn(httpClient, 'put').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'put').and.returnValue( + throwError(() => mockJaloError) + ); let result: HttpErrorModel | undefined; const subscription = service @@ -224,7 +228,7 @@ describe(`OccCheckoutDeliveryModesAdapter`, () => { if (calledTimes === 3) { return of(cartData); } - return throwError(mockJaloError); + return throwError(() => mockJaloError); }) ); @@ -277,7 +281,9 @@ describe(`OccCheckoutDeliveryModesAdapter`, () => { describe(`back-off`, () => { it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { - spyOn(httpClient, 'delete').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'delete').and.returnValue( + throwError(() => mockJaloError) + ); let result: HttpErrorModel | undefined; const subscription = service @@ -301,7 +307,7 @@ describe(`OccCheckoutDeliveryModesAdapter`, () => { if (calledTimes === 3) { return of(checkoutData); } - return throwError(mockJaloError); + return throwError(() => mockJaloError); }) ); diff --git a/feature-libs/checkout/base/occ/adapters/occ-checkout-delivery-modes.adapter.ts b/feature-libs/checkout/base/occ/adapters/occ-checkout-delivery-modes.adapter.ts index d10d7959a32..93d8b96974c 100644 --- a/feature-libs/checkout/base/occ/adapters/occ-checkout-delivery-modes.adapter.ts +++ b/feature-libs/checkout/base/occ/adapters/occ-checkout-delivery-modes.adapter.ts @@ -20,7 +20,7 @@ import { isJaloError, normalizeHttpError, } from '@spartacus/core'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; @Injectable() @@ -43,9 +43,9 @@ export class OccCheckoutDeliveryModesAdapter return this.http .put(this.getSetDeliveryModeEndpoint(userId, cartId, deliveryModeId), {}) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), backOff({ shouldRetry: isJaloError, }) @@ -73,14 +73,13 @@ export class OccCheckoutDeliveryModesAdapter return this.http .get(this.getDeliveryModesEndpoint(userId, cartId)) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), backOff({ shouldRetry: isJaloError, }), - map((listResponse) => listResponse.deliveryModes), - map((modes) => modes ?? []), + map((listResponse) => listResponse.deliveryModes ?? []), this.converter.pipeableMany(DELIVERY_MODE_NORMALIZER) ); } @@ -98,9 +97,9 @@ export class OccCheckoutDeliveryModesAdapter return this.http .delete(this.getClearDeliveryModeEndpoint(userId, cartId)) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), backOff({ shouldRetry: isJaloError, }) diff --git a/feature-libs/checkout/base/occ/adapters/occ-checkout-payment.adapter.spec.ts b/feature-libs/checkout/base/occ/adapters/occ-checkout-payment.adapter.spec.ts index 643523a0f03..c3dc23f67f6 100644 --- a/feature-libs/checkout/base/occ/adapters/occ-checkout-payment.adapter.spec.ts +++ b/feature-libs/checkout/base/occ/adapters/occ-checkout-payment.adapter.spec.ts @@ -249,7 +249,9 @@ describe('OccCheckoutPaymentAdapter', () => { describe(`back-off`, () => { it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { - spyOn(httpClient, 'put').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'put').and.returnValue( + throwError(() => mockJaloError) + ); let result: HttpErrorModel | undefined; const subscription = service @@ -273,7 +275,7 @@ describe('OccCheckoutPaymentAdapter', () => { if (calledTimes === 3) { return of(cartData); } - return throwError(mockJaloError); + return throwError(() => mockJaloError); }) ); @@ -350,7 +352,9 @@ describe('OccCheckoutPaymentAdapter', () => { describe(`back-off`, () => { it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { - spyOn(httpClient, 'get').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'get').and.returnValue( + throwError(() => mockJaloError) + ); let result: HttpErrorModel | undefined; const subscription = service @@ -374,7 +378,7 @@ describe('OccCheckoutPaymentAdapter', () => { if (calledTimes === 3) { return of(paymentProviderInfo); } - return throwError(mockJaloError); + return throwError(() => mockJaloError); }) ); spyOn(httpClient, 'post').and.returnValues( @@ -432,7 +436,9 @@ describe('OccCheckoutPaymentAdapter', () => { describe(`back-off`, () => { it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { - spyOn(httpClient, 'get').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'get').and.returnValue( + throwError(() => mockJaloError) + ); let result: HttpErrorModel | undefined; const subscription = service['getProviderSubInfo'](userId, cartId) @@ -455,7 +461,7 @@ describe('OccCheckoutPaymentAdapter', () => { if (calledTimes === 3) { return of(cartData); } - return throwError(mockJaloError); + return throwError(() => mockJaloError); }) ); @@ -515,7 +521,9 @@ describe('OccCheckoutPaymentAdapter', () => { describe(`back-off`, () => { it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { - spyOn(httpClient, 'post').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'post').and.returnValue( + throwError(() => mockJaloError) + ); let result: HttpErrorModel | undefined; const subscription = service['createSubWithProvider'](mockUrl, params) @@ -538,7 +546,7 @@ describe('OccCheckoutPaymentAdapter', () => { if (calledTimes === 3) { return of(mockPaymentProvider); } - return throwError(mockJaloError); + return throwError(() => mockJaloError); }) ); @@ -600,7 +608,9 @@ describe('OccCheckoutPaymentAdapter', () => { describe(`back-off`, () => { it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { - spyOn(httpClient, 'post').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'post').and.returnValue( + throwError(() => mockJaloError) + ); let result: HttpErrorModel | undefined; const subscription = service['createSubWithProvider'](mockUrl, params) @@ -623,7 +633,7 @@ describe('OccCheckoutPaymentAdapter', () => { if (calledTimes === 3) { return of(mockPaymentProvider); } - return throwError(mockJaloError); + return throwError(() => mockJaloError); }) ); @@ -683,7 +693,9 @@ describe('OccCheckoutPaymentAdapter', () => { describe(`back-off`, () => { it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { - spyOn(httpClient, 'post').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'post').and.returnValue( + throwError(() => mockJaloError) + ); let result: HttpErrorModel | undefined; const subscription = service['createDetailsWithParameters']( @@ -710,7 +722,7 @@ describe('OccCheckoutPaymentAdapter', () => { if (calledTimes === 3) { return of(mockPaymentDetails); } - return throwError(mockJaloError); + return throwError(() => mockJaloError); }) ); @@ -776,7 +788,9 @@ describe('OccCheckoutPaymentAdapter', () => { describe(`back-off`, () => { it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { - spyOn(httpClient, 'post').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'post').and.returnValue( + throwError(() => mockJaloError) + ); let result: HttpErrorModel | undefined; const subscription = service['createDetailsWithParameters']( @@ -803,7 +817,7 @@ describe('OccCheckoutPaymentAdapter', () => { if (calledTimes === 3) { return of(mockPaymentDetails); } - return throwError(mockJaloError); + return throwError(() => mockJaloError); }) ); @@ -882,7 +896,9 @@ describe('OccCheckoutPaymentAdapter', () => { describe(`back-off`, () => { it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { - spyOn(httpClient, 'get').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'get').and.returnValue( + throwError(() => mockJaloError) + ); let result: HttpErrorModel | undefined; const subscription = service @@ -906,7 +922,7 @@ describe('OccCheckoutPaymentAdapter', () => { if (calledTimes === 3) { return of(cardTypesList); } - return throwError(mockJaloError); + return throwError(() => mockJaloError); }) ); diff --git a/feature-libs/checkout/base/occ/adapters/occ-checkout-payment.adapter.ts b/feature-libs/checkout/base/occ/adapters/occ-checkout-payment.adapter.ts index d7d6c13aec6..27dc88fb4d1 100644 --- a/feature-libs/checkout/base/occ/adapters/occ-checkout-payment.adapter.ts +++ b/feature-libs/checkout/base/occ/adapters/occ-checkout-payment.adapter.ts @@ -23,7 +23,7 @@ import { isJaloError, normalizeHttpError, } from '@spartacus/core'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError, map, mergeMap } from 'rxjs/operators'; @Injectable() @@ -79,9 +79,9 @@ export class OccCheckoutPaymentAdapter implements CheckoutPaymentAdapter { cartId, fromPaymentProvider ).pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), backOff({ shouldRetry: isJaloError, }), @@ -104,9 +104,9 @@ export class OccCheckoutPaymentAdapter implements CheckoutPaymentAdapter { {} ) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), backOff({ shouldRetry: isJaloError, }) @@ -128,9 +128,9 @@ export class OccCheckoutPaymentAdapter implements CheckoutPaymentAdapter { return this.http .get(this.getPaymentCardTypesEndpoint()) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), backOff({ shouldRetry: isJaloError, }), @@ -150,9 +150,9 @@ export class OccCheckoutPaymentAdapter implements CheckoutPaymentAdapter { return this.http .get(this.getPaymentProviderSubInfoEndpoint(userId, cartId)) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), backOff({ shouldRetry: isJaloError, }) @@ -190,9 +190,9 @@ export class OccCheckoutPaymentAdapter implements CheckoutPaymentAdapter { responseType: 'text', }) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), backOff({ shouldRetry: isJaloError, }) @@ -220,9 +220,9 @@ export class OccCheckoutPaymentAdapter implements CheckoutPaymentAdapter { { headers } ) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), backOff({ shouldRetry: isJaloError, }) diff --git a/feature-libs/checkout/base/occ/adapters/occ-checkout.adapter.spec.ts b/feature-libs/checkout/base/occ/adapters/occ-checkout.adapter.spec.ts index 678ef6780ba..98f77e86ea4 100644 --- a/feature-libs/checkout/base/occ/adapters/occ-checkout.adapter.spec.ts +++ b/feature-libs/checkout/base/occ/adapters/occ-checkout.adapter.spec.ts @@ -108,7 +108,9 @@ describe('OccCheckoutAdapter', () => { describe(`back-off`, () => { it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { - spyOn(httpClient, 'get').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'get').and.returnValue( + throwError(() => mockJaloError) + ); let result: HttpErrorModel | undefined; const subscription = service @@ -132,7 +134,7 @@ describe('OccCheckoutAdapter', () => { if (calledTimes === 3) { return of(checkoutData); } - return throwError(mockJaloError); + return throwError(() => mockJaloError); }) ); diff --git a/feature-libs/checkout/base/occ/adapters/occ-checkout.adapter.ts b/feature-libs/checkout/base/occ/adapters/occ-checkout.adapter.ts index d5b7a94bcb7..b0c49a345a4 100644 --- a/feature-libs/checkout/base/occ/adapters/occ-checkout.adapter.ts +++ b/feature-libs/checkout/base/occ/adapters/occ-checkout.adapter.ts @@ -19,7 +19,7 @@ import { isJaloError, normalizeHttpError, } from '@spartacus/core'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; @Injectable() @@ -39,9 +39,9 @@ export class OccCheckoutAdapter implements CheckoutAdapter { return this.http .get(this.getGetCheckoutDetailsEndpoint(userId, cartId)) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), backOff({ shouldRetry: isJaloError, }), diff --git a/feature-libs/checkout/base/root/http-interceptors/checkout-cart.interceptor.spec.ts b/feature-libs/checkout/base/root/http-interceptors/checkout-cart.interceptor.spec.ts index eca46ca5ffb..61674bf2e73 100644 --- a/feature-libs/checkout/base/root/http-interceptors/checkout-cart.interceptor.spec.ts +++ b/feature-libs/checkout/base/root/http-interceptors/checkout-cart.interceptor.spec.ts @@ -25,7 +25,7 @@ const cartNotFoundError = { }; class MockRoutingService implements Partial { - go = createSpy().and.returnValue(EMPTY.toPromise()); + go = createSpy().and.returnValue(Promise.resolve(undefined)); getRouterState = createSpy().and.returnValue(EMPTY); } @@ -128,7 +128,7 @@ describe('CheckoutCartInterceptor', () => { function createRequest(): TestRequest { http .get('/test') - .pipe(catchError((error: any) => throwError(error))) + .pipe(catchError((error: any) => throwError(() => error))) .subscribe({ next: () => {}, error: () => {}, diff --git a/feature-libs/checkout/base/root/http-interceptors/checkout-cart.interceptor.ts b/feature-libs/checkout/base/root/http-interceptors/checkout-cart.interceptor.ts index 67c8878ee42..3a9e0094b00 100644 --- a/feature-libs/checkout/base/root/http-interceptors/checkout-cart.interceptor.ts +++ b/feature-libs/checkout/base/root/http-interceptors/checkout-cart.interceptor.ts @@ -14,8 +14,8 @@ import { import { Injectable } from '@angular/core'; import { MultiCartFacade } from '@spartacus/cart/base/root'; import { RouterState, RoutingService } from '@spartacus/core'; -import { Observable, throwError } from 'rxjs'; -import { catchError, switchMap, take } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { switchMap, take, tap } from 'rxjs/operators'; /** * Interceptor that handles "Cart not found" errors while a user is in a checkout step. @@ -38,21 +38,22 @@ export class CheckoutCartInterceptor implements HttpInterceptor { take(1), switchMap((state: RouterState) => { return next.handle(request).pipe( - catchError((response) => { - if ( - response instanceof HttpErrorResponse && - this.isUserInCheckoutRoute(state.state?.semanticRoute) - ) { - if (this.isCartNotFoundError(response)) { - this.routingService.go({ cxRoute: 'cart' }); + tap({ + error: (response) => { + if ( + response instanceof HttpErrorResponse && + this.isUserInCheckoutRoute(state.state?.semanticRoute) + ) { + if (this.isCartNotFoundError(response)) { + this.routingService.go({ cxRoute: 'cart' }); - const cartCode = this.getCartIdFromError(response); - if (cartCode) { - this.multiCartFacade.reloadCart(cartCode); + const cartCode = this.getCartIdFromError(response); + if (cartCode) { + this.multiCartFacade.reloadCart(cartCode); + } } } - } - return throwError(response); + }, }) ); }) diff --git a/feature-libs/checkout/package.json b/feature-libs/checkout/package.json index 6d20fa39f61..d5d3959f2a5 100644 --- a/feature-libs/checkout/package.json +++ b/feature-libs/checkout/package.json @@ -40,7 +40,7 @@ "@spartacus/styles": "6.6.0-1", "@spartacus/user": "6.6.0-1", "bootstrap": "^4.6.2", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/feature-libs/checkout/scheduled-replenishment/components/checkout-place-order/checkout-place-order.component.spec.ts b/feature-libs/checkout/scheduled-replenishment/components/checkout-place-order/checkout-place-order.component.spec.ts index 83b5aa7873b..8d5ba5f6d19 100644 --- a/feature-libs/checkout/scheduled-replenishment/components/checkout-place-order/checkout-place-order.component.spec.ts +++ b/feature-libs/checkout/scheduled-replenishment/components/checkout-place-order/checkout-place-order.component.spec.ts @@ -64,7 +64,7 @@ class MockCheckoutReplenishmentFormService } class MockRoutingService implements Partial { - go = createSpy().and.returnValue(of(true).toPromise()); + go = createSpy().and.returnValue(Promise.resolve(true)); } class MockLaunchDialogService implements Partial { diff --git a/feature-libs/customer-ticketing/components/details/customer-ticketing-messages/customer-ticketing-messages.component.spec.ts b/feature-libs/customer-ticketing/components/details/customer-ticketing-messages/customer-ticketing-messages.component.spec.ts index 3f64fca8263..7a4b6d2f685 100644 --- a/feature-libs/customer-ticketing/components/details/customer-ticketing-messages/customer-ticketing-messages.component.spec.ts +++ b/feature-libs/customer-ticketing/components/details/customer-ticketing-messages/customer-ticketing-messages.component.spec.ts @@ -1,9 +1,13 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { EventService, I18nTestingModule } from '@spartacus/core'; import { + CustomerTicketingConfig, CustomerTicketingFacade, + STATUS_NAME, + TicketDetails, TicketEvent, } from '@spartacus/customer-ticketing/root'; +import { MessageEvent } from '@spartacus/storefront'; import { BehaviorSubject, EMPTY } from 'rxjs'; import { CustomerTicketingMessagesComponent } from './customer-ticketing-messages.component'; import createSpy = jasmine.createSpy; @@ -21,12 +25,13 @@ describe('CustomerTicketMessagesComponent', () => { const mockResponse = { message: mockSendEvent.message, code: 'mockCode' }; const createTicketResponse$ = new BehaviorSubject({}); + const getTicket$ = new BehaviorSubject({}); class MockCustomerTicketingFacade implements Partial { createTicketEvent = () => createTicketResponse$; - getTicket = createSpy().and.returnValue(EMPTY); + getTicket = createSpy().and.returnValue(getTicket$.asObservable()); downloadAttachment = createSpy().and.returnValue(EMPTY); uploadAttachment = createSpy().and.returnValue(EMPTY); } @@ -119,4 +124,60 @@ describe('CustomerTicketMessagesComponent', () => { 'mockId' ); }); + + describe('messaging', () => { + let mockTicketDetails: TicketDetails; + + beforeEach(() => { + mockTicketDetails = { + ticketEvents: [ + { + createdAt: 'mock-create-date', + author: 'mock-author', + message: 'mock-message', + addedByAgent: true, + ticketEventAttachments: [{}], + }, + ], + status: { id: 'mock-status-id', name: STATUS_NAME.OPEN }, + }; + + getTicket$.next(mockTicketDetails); + }); + + it('should provide the ticket events as messages', () => { + const expected: MessageEvent[] = [ + { + createdAt: 'mock-create-date', + author: 'mock-author', + message: 'mock-message', + addedByAgent: true, + ticketEventAttachments: [{}], + text: 'mock-message', + rightAlign: true, + attachments: [{}], + } as MessageEvent & { message: string }, + ]; + + component.messageEvents$.subscribe((actual) => { + expect(actual).toEqual(expected); + }); + }); + + it('should generate a messages config', () => { + const customerTicketingConfig = TestBed.inject(CustomerTicketingConfig); + const actual = component.messagingConfigs; + + actual.displayAddMessageSection?.subscribe((displayAddMessageSection) => + expect(displayAddMessageSection).toBe(true) + ); + expect(actual.attachmentRestrictions).toEqual( + customerTicketingConfig.customerTicketing?.attachmentRestrictions + ); + expect(actual.charactersLimit).toEqual( + customerTicketingConfig.customerTicketing?.inputCharactersLimit + ); + expect(actual.enableFileUploadOption).toBe(true); + }); + }); }); diff --git a/feature-libs/customer-ticketing/components/details/customer-ticketing-reopen/customer-ticketing-reopen-dialog/customer-ticketing-reopen-dialog.component.spec.ts b/feature-libs/customer-ticketing/components/details/customer-ticketing-reopen/customer-ticketing-reopen-dialog/customer-ticketing-reopen-dialog.component.spec.ts index 564d1a6fdbc..d35344bc525 100644 --- a/feature-libs/customer-ticketing/components/details/customer-ticketing-reopen/customer-ticketing-reopen-dialog/customer-ticketing-reopen-dialog.component.spec.ts +++ b/feature-libs/customer-ticketing/components/details/customer-ticketing-reopen/customer-ticketing-reopen-dialog/customer-ticketing-reopen-dialog.component.spec.ts @@ -4,9 +4,10 @@ import { CustomerTicketingFacade, STATUS, STATUS_NAME, + TicketEvent, } from '@spartacus/customer-ticketing/root'; import { LaunchDialogService } from '@spartacus/storefront'; -import { EMPTY } from 'rxjs'; +import { EMPTY, of } from 'rxjs'; import { CustomerTicketingReopenDialogComponent } from './customer-ticketing-reopen-dialog.component'; import createSpy = jasmine.createSpy; @@ -15,6 +16,7 @@ class MockLaunchDialogService implements Partial { } class MockCustomerTicketingFacade implements Partial { createTicketEvent = createSpy().and.returnValue(EMPTY); + uploadAttachment = createSpy().and.returnValue(EMPTY); } class MockRoutingService implements Partial { @@ -66,23 +68,53 @@ describe('CustomerTicketingReopenDialogComponent', () => { expect(customerTicketingFacade.createTicketEvent).not.toHaveBeenCalled(); }); - it('should call createTicketEvent if the form is valid', () => { - const mockEvent = { - message: 'mockMessage', - toStatus: { - id: STATUS.INPROCESS, - name: STATUS_NAME.INPROCESS, - }, - }; - const mustWaitForAttachment = false; + describe('when the form is valid', () => { + beforeEach(() => { + component.form.get('message')?.setValue('mockMessage'); + }); - component.form.get('message')?.setValue('mockMessage'); - component.reopenRequest(); + it('should call createTicketEvent if the form is valid', () => { + const mockEvent = { + message: 'mockMessage', + toStatus: { + id: STATUS.INPROCESS, + name: STATUS_NAME.INPROCESS, + }, + }; + const mustWaitForAttachment = false; + + component.reopenRequest(); + + expect(customerTicketingFacade.createTicketEvent).toHaveBeenCalledWith( + mockEvent, + mustWaitForAttachment + ); + }); + + it('should upload attachements after creating ticket', () => { + const mockFileList: File[] = [ + new File(['foo'], 'foo.txt', { + type: 'text/plain', + }), + ]; + (mockFileList as any).item = (i: number) => mockFileList[i]; // mock FileList's accessor + component.form.get('file')?.setValue(mockFileList); + const mockTicketEvent: TicketEvent = { + code: 'code-000001', + createdAt: 'mock-create-date', + author: 'mock-author', + message: 'mock-message', + addedByAgent: true, + ticketEventAttachments: [{}], + }; + ( + customerTicketingFacade.createTicketEvent as jasmine.Spy + ).and.returnValue(of(mockTicketEvent)); + + component.reopenRequest(); - expect(customerTicketingFacade.createTicketEvent).toHaveBeenCalledWith( - mockEvent, - mustWaitForAttachment - ); + expect(customerTicketingFacade.uploadAttachment).toHaveBeenCalled(); + }); }); }); }); diff --git a/feature-libs/customer-ticketing/components/list/customer-ticketing-create/customer-ticketing-create-dialog/customer-ticketing-create-dialog.component.spec.ts b/feature-libs/customer-ticketing/components/list/customer-ticketing-create/customer-ticketing-create-dialog/customer-ticketing-create-dialog.component.spec.ts index a3ac259d1be..85ca6db179c 100644 --- a/feature-libs/customer-ticketing/components/list/customer-ticketing-create/customer-ticketing-create-dialog/customer-ticketing-create-dialog.component.spec.ts +++ b/feature-libs/customer-ticketing/components/list/customer-ticketing-create/customer-ticketing-create-dialog/customer-ticketing-create-dialog.component.spec.ts @@ -2,10 +2,12 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { I18nTestingModule, RoutingService } from '@spartacus/core'; import { CustomerTicketingFacade, + STATUS_NAME, + TicketDetails, TicketStarter, } from '@spartacus/customer-ticketing/root'; import { LaunchDialogService } from '@spartacus/storefront'; -import { EMPTY, of } from 'rxjs'; +import { EMPTY, of, throwError } from 'rxjs'; import { CustomerTicketingCreateDialogComponent } from './customer-ticketing-create-dialog.component'; import createSpy = jasmine.createSpy; @@ -52,6 +54,7 @@ class MockCustomerTicketingFacade implements Partial { getTicketAssociatedObjects = createSpy().and.returnValue( of(mockTicketAssociatedObjects) ); + uploadAttachment = createSpy().and.returnValue(EMPTY); } describe('CustomerTicketingCreateDialogComponent', () => { @@ -94,14 +97,64 @@ describe('CustomerTicketingCreateDialogComponent', () => { }); describe('trigger create ticket request', () => { - it('should call createTicket if the form is valid', () => { - component.form.get('message')?.setValue(mockTicketStarter.message); - component.form.get('subject')?.setValue(mockTicketStarter.subject); - component.form.get('ticketCategory')?.setValue(mockCategories); - component.form.get('associatedTo')?.setValue(mockTicketAssociatedObjects); - component.createTicketRequest(); + describe('when the form is valid', () => { + beforeEach(() => { + component.form.get('message')?.setValue(mockTicketStarter.message); + component.form.get('subject')?.setValue(mockTicketStarter.subject); + component.form.get('ticketCategory')?.setValue(mockCategories); + component.form + .get('associatedTo') + ?.setValue(mockTicketAssociatedObjects); + }); + + it('should call createTicket if the form is valid', () => { + component.createTicketRequest(); + + expect(customerTicketingFacade.createTicket).toHaveBeenCalled(); + }); + + it('should upload attachments after creating the ticket', () => { + const mockFileList: File[] = [ + new File(['foo'], 'foo.txt', { + type: 'text/plain', + }), + ]; + component.form.get('file')?.setValue(mockFileList); + const mockTicketDetails: TicketDetails = { + id: '000001', + + status: { id: 'mock-status-id', name: STATUS_NAME.OPEN }, + ticketEvents: [ + { + code: 'code-000001', + createdAt: 'mock-create-date', + author: 'mock-author', + message: 'mock-message', + addedByAgent: true, + ticketEventAttachments: [{}], + }, + ], + }; + + (customerTicketingFacade.createTicket as jasmine.Spy).and.returnValue( + of(mockTicketDetails) + ); + + component.createTicketRequest(); + + expect(customerTicketingFacade.uploadAttachment).toHaveBeenCalled(); + }); + + it('should close if there is an error creating the ticket', () => { + spyOn(component, 'close').and.callThrough(); + (customerTicketingFacade.createTicket as jasmine.Spy).and.returnValue( + throwError(() => 'error') + ); + + component.createTicketRequest(); - expect(customerTicketingFacade.createTicket).toHaveBeenCalled(); + expect(component.close).toHaveBeenCalledWith('Something went wrong'); + }); }); it('should not call createTicket if the form is invalid', () => { diff --git a/feature-libs/customer-ticketing/components/list/customer-ticketing-create/customer-ticketing-create.component.spec.ts b/feature-libs/customer-ticketing/components/list/customer-ticketing-create/customer-ticketing-create.component.spec.ts index 3a326d2f545..ca56c187187 100644 --- a/feature-libs/customer-ticketing/components/list/customer-ticketing-create/customer-ticketing-create.component.spec.ts +++ b/feature-libs/customer-ticketing/components/list/customer-ticketing-create/customer-ticketing-create.component.spec.ts @@ -1,7 +1,7 @@ import { ElementRef, ViewContainerRef } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { I18nTestingModule } from '@spartacus/core'; -import { LaunchDialogService, LAUNCH_CALLER } from '@spartacus/storefront'; +import { LAUNCH_CALLER, LaunchDialogService } from '@spartacus/storefront'; import { EMPTY } from 'rxjs'; import { CustomerTicketingCreateComponent } from './customer-ticketing-create.component'; @@ -42,7 +42,7 @@ describe('CustomerTicketingCreateComponent', () => { expect(component).toBeTruthy(); }); it('should trigger create new ticket dialog', () => { - spyOn(launchDialogService, 'openDialog'); + spyOn(launchDialogService, 'openDialog').and.callThrough(); component.openCreateNewTicketDialog(); expect(launchDialogService.openDialog).toHaveBeenCalledWith( diff --git a/feature-libs/customer-ticketing/components/list/customer-ticketing-list/customer-ticketing-list.component.spec.ts b/feature-libs/customer-ticketing/components/list/customer-ticketing-list/customer-ticketing-list.component.spec.ts index f2aec10c26d..285f41adeae 100644 --- a/feature-libs/customer-ticketing/components/list/customer-ticketing-list/customer-ticketing-list.component.spec.ts +++ b/feature-libs/customer-ticketing/components/list/customer-ticketing-list/customer-ticketing-list.component.spec.ts @@ -14,17 +14,20 @@ import { RoutingService, TranslationService, } from '@spartacus/core'; -import { TicketList } from '@spartacus/customer-ticketing/root'; -import { BehaviorSubject, EMPTY, Observable, of } from 'rxjs'; +import { + CustomerTicketingFacade, + TicketList, +} from '@spartacus/customer-ticketing/root'; +import { EMPTY, Observable, of } from 'rxjs'; import { CustomerTicketingListComponent } from './customer-ticketing-list.component'; const mockTicketList: TicketList = { pagination: { currentPage: 0, - pageSize: 5, + pageSize: 2, sort: 'byId', - totalPages: 1, - totalResults: 2, + totalPages: 2, + totalResults: 3, }, sorts: [ { code: 'byId', selected: true }, @@ -96,6 +99,54 @@ const mockTicketList: TicketList = { }, ], }; + +const mockTicketList2: TicketList = { + pagination: { + currentPage: 1, + pageSize: 2, + sort: 'byId', + totalPages: 2, + totalResults: 3, + }, + sorts: [ + { code: 'byId', selected: true }, + { code: 'byChangedDate', selected: false }, + ], + tickets: [ + { + availableStatusTransitions: [ + { + id: 'CLOSED', + name: 'Closed', + }, + ], + id: '0000003', + createdAt: '2021-01-14T10:06:57+0000', + modifiedAt: '2021-01-14T10:06:57+0000', + status: { + id: 'OPEN', + name: 'Open', + }, + subject: 'Lawnmower blade gone', + ticketCategory: { + id: 'ENQUIRY', + name: 'Enquiry', + }, + ticketEvents: [ + { + author: 'Bob', + createdAt: '2021-01-14T10:06:57+0000', + message: 'Mower missing blade', + toStatus: { + id: 'OPEN', + name: 'Open', + }, + }, + ], + }, + ], +}; + @Component({ template: '', selector: 'cx-pagination', @@ -133,24 +184,23 @@ class MockTranslationService { } } -class MockcustomerTicketingFacade { +class MockCustomerTicketingFacade { getTickets( _pageSize: number, _currentPage?: number, _sort?: string ): Observable { - return mockTicketList$.asObservable(); + return of(mockTicketList); } clearTicketList() {} } -const mockTicketList$ = new BehaviorSubject(mockTicketList); - describe('CustomerTicketingListComponent', () => { let component: CustomerTicketingListComponent; let fixture: ComponentFixture; let routingService: RoutingService; + let customerTicketingFacade: CustomerTicketingFacade; beforeEach( waitForAsync(() => { @@ -164,17 +214,30 @@ describe('CustomerTicketingListComponent', () => { ], providers: [ { - provide: 'CustomerTicketingFacade', - useClass: MockcustomerTicketingFacade, + provide: CustomerTicketingFacade, + useClass: MockCustomerTicketingFacade, }, { provide: RoutingService, useClass: MockRoutingService }, { provide: TranslationService, useClass: MockTranslationService }, ], }).compileComponents(); + const translationService = TestBed.inject(TranslationService); + spyOn(translationService, 'translate').and.callFake((input) => { + switch (input) { + case 'customerTicketing.ticketId': + return of('ticket-id'); + case 'customerTicketing.changedOn': + return of(new Date(0).toISOString()); + default: + return EMPTY; + } + }); + + customerTicketingFacade = TestBed.inject(CustomerTicketingFacade); + fixture = TestBed.createComponent(CustomerTicketingListComponent); component = fixture.componentInstance; - component.tickets$ = of(mockTicketList); routingService = TestBed.inject(RoutingService); fixture.detectChanges(); }) @@ -185,7 +248,7 @@ describe('CustomerTicketingListComponent', () => { }); it('should display tickets', () => { - const TWO_TICKETS = '(2)'; + const TWO_TICKETS = '(3)'; const ticketsCount = fixture.debugElement.query( By.css('.cx-ticketing-list-title-text') @@ -212,10 +275,24 @@ describe('CustomerTicketingListComponent', () => { expect(routingService.go).toHaveBeenCalledWith({ cxRoute: 'supportTicketDetails', - params: - mockTicketList && mockTicketList.tickets - ? { ticketCode: mockTicketList.tickets[1].id } - : {}, + params: mockTicketList?.tickets + ? { ticketCode: mockTicketList.tickets[1].id } + : {}, }); }); + + it('should display next page', () => { + spyOn(customerTicketingFacade, 'getTickets').and.returnValue( + of(mockTicketList2) + ); + + component.pageChange(1); + + fixture.detectChanges(); + const idElements = fixture.debugElement + .queryAll(By.css('.cx-ticketing-list-id a.cx-ticketing-list-value')) + .map((debugElement) => debugElement.nativeElement as HTMLElement); + expect(idElements.length).toBe(1); + expect(idElements[0].textContent?.includes('0000003')).toBe(true); + }); }); diff --git a/feature-libs/customer-ticketing/core/http-interceptors/handlers/not-found-ticket-request.handler.spec.ts b/feature-libs/customer-ticketing/core/http-interceptors/handlers/not-found-ticket-request.handler.spec.ts index 60c24df4799..d78b4ba5722 100644 --- a/feature-libs/customer-ticketing/core/http-interceptors/handlers/not-found-ticket-request.handler.spec.ts +++ b/feature-libs/customer-ticketing/core/http-interceptors/handlers/not-found-ticket-request.handler.spec.ts @@ -1,18 +1,37 @@ -import { HttpErrorResponse, HttpRequest } from '@angular/common/http'; +import { + HttpErrorResponse, + HttpHeaders, + HttpRequest, +} from '@angular/common/http'; import { TestBed } from '@angular/core/testing'; import { + ErrorModel, GlobalMessageService, GlobalMessageType, HttpResponseStatus, + OccHttpErrorReason, + OccHttpErrorType, + Priority, + RouterState, RoutingService, } from '@spartacus/core'; +import { of } from 'rxjs'; import { NotFoundTicketRequestHandler } from './not-found-ticket-request.handler'; -const MockRequest = {} as HttpRequest; +/** Taken from the constructor argument of `HttpErrorResponse` */ +type HttpErrorResponseInit = { + error?: any; + headers?: HttpHeaders; + status?: number; + statusText?: string; + url?: string; +}; + +const mockRequest = {} as HttpRequest; -const MockRandomResponse = {} as HttpErrorResponse; +const mockRandomResponse = {} as HttpErrorResponse; -const MockTicketNotFoundResponse = { +const mockTicketNotFoundResponse = { error: { errors: [ { @@ -63,17 +82,23 @@ describe('NotFoundTicketRequestHandler', () => { expect(service).toBeTruthy(); }); + it('should have a priority of medium', () => { + expect(service.getPriority()).toEqual(Priority.NORMAL); + }); + it('should register 404 responseStatus', () => { expect(service.responseStatus).toEqual(HttpResponseStatus.NOT_FOUND); }); it('should not handle response without errors', () => { - service.handleError(MockRequest, MockRandomResponse); + service.handleError(mockRequest, mockRandomResponse); + expect(globalMessageService.add).not.toHaveBeenCalled(); }); it('should handle ticket not found error', () => { - service.handleError(MockRequest, MockTicketNotFoundResponse); + service.handleError(mockRequest, mockTicketNotFoundResponse); + expect(routingService.go).toHaveBeenCalledWith({ cxRoute: 'supportTickets', }); @@ -82,4 +107,47 @@ describe('NotFoundTicketRequestHandler', () => { GlobalMessageType.MSG_TYPE_ERROR ); }); + + describe('hasMatch', () => { + let errorResponseInit: HttpErrorResponseInit; + + describe('when statis is NOT_FOUND', () => { + beforeEach(() => { + errorResponseInit = { + status: 404, + statusText: 'NOT FOUND', + }; + }); + + describe('when the route is supportTicketDetails', () => { + beforeEach(() => { + routingService.getRouterState; + spyOn(routingService, 'getRouterState').and.returnValue( + of({ + state: { semanticRoute: 'supportTicketDetails' }, + } as RouterState) + ); + }); + + describe('when the error response contains an error of type notFound', () => { + beforeEach(() => { + errorResponseInit.error = { + errors: [ + { + type: OccHttpErrorType.NOT_FOUND_ERROR, + reason: OccHttpErrorReason.NOT_FOUND_ERROR, + } as ErrorModel, + ], + }; + }); + + it('should match to the error', () => { + const httpErrorResponse = new HttpErrorResponse(errorResponseInit); + + expect(service.hasMatch(httpErrorResponse)).toBe(true); + }); + }); + }); + }); + }); }); diff --git a/feature-libs/customer-ticketing/karma.conf.js b/feature-libs/customer-ticketing/karma.conf.js index aa436f39398..3a6e5bf9c81 100644 --- a/feature-libs/customer-ticketing/karma.conf.js +++ b/feature-libs/customer-ticketing/karma.conf.js @@ -4,19 +4,14 @@ module.exports = function (config) { config.set({ basePath: '', - frameworks: ['parallel', 'jasmine', '@angular-devkit/build-angular'], + frameworks: ['jasmine', '@angular-devkit/build-angular'], plugins: [ - require('karma-parallel'), require('karma-jasmine'), require('karma-coverage'), require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), require('@angular-devkit/build-angular/plugins/karma'), ], - parallelOptions: { - executors: 2, - shardStrategy: 'round-robin', - }, client: { clearContext: false, // leave Jasmine Spec Runner output visible in browser jasmine: { diff --git a/feature-libs/customer-ticketing/occ/adapters/occ-customer-ticketing.adapter.spec.ts b/feature-libs/customer-ticketing/occ/adapters/occ-customer-ticketing.adapter.spec.ts index 27b38e5fbda..d0b7db75fc7 100644 --- a/feature-libs/customer-ticketing/occ/adapters/occ-customer-ticketing.adapter.spec.ts +++ b/feature-libs/customer-ticketing/occ/adapters/occ-customer-ticketing.adapter.spec.ts @@ -3,18 +3,26 @@ import { HttpTestingController, } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; -import { ConverterService, OccConfig, OccEndpoints } from '@spartacus/core'; import { - TicketDetails, - TicketEvent, - TicketList, - TicketStarter, -} from '@spartacus/customer-ticketing/root'; + ConverterService, + HttpErrorModel, + OccConfig, + OccEndpoints, +} from '@spartacus/core'; import { CUSTOMER_TICKETING_CREATE_NORMALIZER, CUSTOMER_TICKETING_EVENT_NORMALIZER, CUSTOMER_TICKETING_FILE_NORMALIZER, } from '@spartacus/customer-ticketing/core'; +import { + AssociatedObjectsList, + CategoriesList, + Category, + TicketDetails, + TicketEvent, + TicketList, + TicketStarter, +} from '@spartacus/customer-ticketing/root'; import { take } from 'rxjs/operators'; import { OccCustomerTicketingAdapter } from './occ-customer-ticketing.adapter'; @@ -365,4 +373,83 @@ describe('OccCustomerTicketingAdapter', () => { ); }); }); + + describe('getTicketAssociatedObjects', () => { + it('should send a request to the occ API', () => { + const response: AssociatedObjectsList = { + ticketAssociatedObjects: [ + { code: 'mock-code', type: 'mock-type', modifiedAt: '' }, + ], + }; + + service.getTicketAssociatedObjects(mockCustomerId).subscribe((actual) => { + expect(actual).toEqual(response.ticketAssociatedObjects); + }); + + httpMock + .expectOne({ + method: 'GET', + url: `users/${mockCustomerId}/ticketAssociatedObjects`, + }) + .flush(response); + }); + + it('should handle an error from the occ API', () => { + const errorStatus = 500; + const errorText = 'INTERNAL SERVER ERROR'; + + service.getTicketAssociatedObjects(mockCustomerId).subscribe({ + error: (errObject) => { + expect(errObject instanceof HttpErrorModel).toBe(true); + expect((errObject as HttpErrorModel).status).toBe(errorStatus); + expect((errObject as HttpErrorModel).statusText).toEqual(errorText); + }, + }); + + httpMock + .expectOne({ + method: 'GET', + url: `users/${mockCustomerId}/ticketAssociatedObjects`, + }) + .flush({}, { status: errorStatus, statusText: errorText }); + }); + }); + describe('getTicketCategories', () => { + it('should send a request to the occ API', () => { + const response: CategoriesList = { + ticketCategories: [{ id: 'mock-id', name: 'mock-name' }], + }; + + service.getTicketCategories().subscribe((actual) => { + expect(actual).toEqual(response.ticketCategories as Category[]); + }); + + httpMock + .expectOne({ + method: 'GET', + url: `/ticketCategories`, + }) + .flush(response); + }); + + it('should handle an error from the occ API', () => { + const errorStatus = 500; + const errorText = 'INTERNAL SERVER ERROR'; + + service.getTicketCategories().subscribe({ + error: (errObject) => { + expect(errObject instanceof HttpErrorModel).toBe(true); + expect((errObject as HttpErrorModel).status).toBe(errorStatus); + expect((errObject as HttpErrorModel).statusText).toEqual(errorText); + }, + }); + + httpMock + .expectOne({ + method: 'GET', + url: `/ticketCategories`, + }) + .flush({}, { status: errorStatus, statusText: errorText }); + }); + }); }); diff --git a/feature-libs/customer-ticketing/occ/adapters/occ-customer-ticketing.adapter.ts b/feature-libs/customer-ticketing/occ/adapters/occ-customer-ticketing.adapter.ts index 3a964290d74..7a8ea74a0ee 100644 --- a/feature-libs/customer-ticketing/occ/adapters/occ-customer-ticketing.adapter.ts +++ b/feature-libs/customer-ticketing/occ/adapters/occ-customer-ticketing.adapter.ts @@ -32,7 +32,7 @@ import { TicketList, TicketStarter, } from '@spartacus/customer-ticketing/root'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError, map, tap } from 'rxjs/operators'; @Injectable() @@ -52,9 +52,9 @@ export class OccCustomerTicketingAdapter implements CustomerTicketingAdapter { this.getTicketAssociatedObjectsEndpoint(customerId) ) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), map( (associatedObjectList) => associatedObjectList.ticketAssociatedObjects ?? [] @@ -77,9 +77,9 @@ export class OccCustomerTicketingAdapter implements CustomerTicketingAdapter { return this.http .get(this.getTicketCategoriesEndpoint()) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), map((categoryList) => categoryList.ticketCategories ?? []), this.converter.pipeableMany(CUSTOMER_TICKETING_CATEGORY_NORMALIZER) ); @@ -94,7 +94,7 @@ export class OccCustomerTicketingAdapter implements CustomerTicketingAdapter { .get(this.getTicketEndpoint(customerId, ticketId)) .pipe( catchError((errorResponse) => { - return throwError(normalizeHttpError(errorResponse, this.logger)); + throw normalizeHttpError(errorResponse); }), tap((ticket) => ticket.ticketEvents?.reverse()), this.converter.pipeable(CUSTOMER_TICKETING_DETAILS_NORMALIZER) @@ -119,9 +119,9 @@ export class OccCustomerTicketingAdapter implements CustomerTicketingAdapter { headers: new HttpHeaders().set('Content-Type', 'application/json'), }) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), this.converter.pipeable(CUSTOMER_TICKETING_CREATE_NORMALIZER) ); } @@ -145,9 +145,9 @@ export class OccCustomerTicketingAdapter implements CustomerTicketingAdapter { this.getTicketsEndpoint(customerId, pageSize, currentPage, sort) ) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), this.converter.pipeable(CUSTOMER_TICKETING_LIST_NORMALIZER) ); } @@ -184,9 +184,9 @@ export class OccCustomerTicketingAdapter implements CustomerTicketingAdapter { } ) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), this.converter.pipeable(CUSTOMER_TICKETING_EVENT_NORMALIZER) ); } @@ -218,9 +218,9 @@ export class OccCustomerTicketingAdapter implements CustomerTicketingAdapter { formData ) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), this.converter.pipeable(CUSTOMER_TICKETING_FILE_NORMALIZER) ); } @@ -259,9 +259,9 @@ export class OccCustomerTicketingAdapter implements CustomerTicketingAdapter { httpOptions ) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), this.converter.pipeable(CUSTOMER_TICKETING_FILE_NORMALIZER) ); } diff --git a/feature-libs/customer-ticketing/package.json b/feature-libs/customer-ticketing/package.json index 87ae3637da5..34b7fee7f46 100644 --- a/feature-libs/customer-ticketing/package.json +++ b/feature-libs/customer-ticketing/package.json @@ -35,7 +35,7 @@ "@spartacus/schematics": "6.6.0-1", "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/feature-libs/order/core/facade/order-history.service.spec.ts b/feature-libs/order/core/facade/order-history.service.spec.ts index 994e95ac359..743b7750681 100644 --- a/feature-libs/order/core/facade/order-history.service.spec.ts +++ b/feature-libs/order/core/facade/order-history.service.spec.ts @@ -174,7 +174,7 @@ describe('OrderHistoryService', () => { it('should NOT load order list data when user is anonymous', () => { spyOn(userIdService, 'takeUserId').and.callFake(() => { - return throwError('Error'); + return throwError(() => 'Error'); }); userOrderService.loadOrderList(10, 1, 'byDate'); diff --git a/feature-libs/order/core/facade/order-return-request.service.spec.ts b/feature-libs/order/core/facade/order-return-request.service.spec.ts index 403d5ac75b5..187970bb4ae 100644 --- a/feature-libs/order/core/facade/order-return-request.service.spec.ts +++ b/feature-libs/order/core/facade/order-return-request.service.spec.ts @@ -149,7 +149,7 @@ describe('OrderReturnRequestService', () => { it('should NOT load order return requests list when user is anonymous', () => { spyOn(userIdService, 'takeUserId').and.callFake(() => { - return throwError('Error'); + return throwError(() => 'Error'); }); orderReturnRequestService.loadOrderReturnRequestList(10, 1, 'byDate'); diff --git a/feature-libs/order/core/facade/reorder-order.service.spec.ts b/feature-libs/order/core/facade/reorder-order.service.spec.ts index 37ed0fa4245..0f7054c72b2 100644 --- a/feature-libs/order/core/facade/reorder-order.service.spec.ts +++ b/feature-libs/order/core/facade/reorder-order.service.spec.ts @@ -5,7 +5,7 @@ import { MultiCartFacade, } from '@spartacus/cart/base/root'; import { OCC_USER_ID_CURRENT, UserIdService } from '@spartacus/core'; -import { of } from 'rxjs'; +import { config, of } from 'rxjs'; import { ReorderOrderConnector } from '../connectors/reorder-order.connector'; import { ReorderOrderService } from './reorder-order.service'; @@ -41,6 +41,22 @@ describe(`ReorderOrderService`, () => { let activeCartFacade: ActiveCartFacade; let multiCartFacade: MultiCartFacade; + // TODO: CXSPA-4870 verify if can be avoided + let originalOnUnhandledError: ((err: any) => void) | null; + + beforeAll(() => { + // configure rxjs to not crash node instance with thrown errors + // TODO: CXSPA-4870 verify if can be avoided + originalOnUnhandledError = config.onUnhandledError; + config.onUnhandledError = () => {}; + }); + + afterAll(() => { + // reset rxjs configuration + // TODO: CXSPA-4870 verify if can be avoided + config.onUnhandledError = originalOnUnhandledError; + }); + beforeEach(() => { TestBed.configureTestingModule({ providers: [ diff --git a/feature-libs/order/core/facade/replenishment-order-history.service.spec.ts b/feature-libs/order/core/facade/replenishment-order-history.service.spec.ts index 1a6d7b48652..ff791305072 100644 --- a/feature-libs/order/core/facade/replenishment-order-history.service.spec.ts +++ b/feature-libs/order/core/facade/replenishment-order-history.service.spec.ts @@ -90,7 +90,7 @@ describe('UserReplenishmentOrderService', () => { it('should NOT be able to load replenishment order details when user is anonymous', () => { spyOn(userIdService, 'takeUserId').and.callFake(() => { - return throwError('Error'); + return throwError(() => 'Error'); }); userReplenishmentOrderService.loadReplenishmentOrderDetails( @@ -191,7 +191,7 @@ describe('UserReplenishmentOrderService', () => { it('should NOT be able to load replenishment order details when user is anonymous', () => { spyOn(userIdService, 'takeUserId').and.callFake(() => { - return throwError('Error'); + return throwError(() => 'Error'); }); userReplenishmentOrderService.cancelReplenishmentOrder( diff --git a/feature-libs/order/core/store/effects/consignment-tracking.effect.spec.ts b/feature-libs/order/core/store/effects/consignment-tracking.effect.spec.ts index c2216c4da47..c9736cc202e 100644 --- a/feature-libs/order/core/store/effects/consignment-tracking.effect.spec.ts +++ b/feature-libs/order/core/store/effects/consignment-tracking.effect.spec.ts @@ -71,7 +71,7 @@ describe('Consignment Tracking effect', () => { it('should handle failures for load consignment tracking', () => { spyOn(orderHistoryConnector, 'getConsignmentTracking').and.returnValue( - throwError('Error') + throwError(() => 'Error') ); const action = new OrderActions.LoadConsignmentTracking( diff --git a/feature-libs/order/core/store/effects/order-details.effect.spec.ts b/feature-libs/order/core/store/effects/order-details.effect.spec.ts index 1b91b4d637b..5d386f5057d 100644 --- a/feature-libs/order/core/store/effects/order-details.effect.spec.ts +++ b/feature-libs/order/core/store/effects/order-details.effect.spec.ts @@ -89,7 +89,9 @@ describe('Order Details effect', () => { }); it('should handle failures for load order details', () => { - spyOn(orderHistoryConnector, 'get').and.returnValue(throwError('Error')); + spyOn(orderHistoryConnector, 'get').and.returnValue( + throwError(() => 'Error') + ); const action = new OrderActions.LoadOrderDetails(mockOrderDetailsParams); @@ -118,7 +120,7 @@ describe('Order Details effect', () => { it('should handle failures for cancel an order', () => { spyOn(orderHistoryConnector, 'cancel').and.returnValue( - throwError('Error') + throwError(() => 'Error') ); const action = new OrderActions.CancelOrder(mockCancelOrderParams); diff --git a/feature-libs/order/core/store/effects/order-return-request.effect.spec.ts b/feature-libs/order/core/store/effects/order-return-request.effect.spec.ts index 17396349c2d..ae4f4e3cb7f 100644 --- a/feature-libs/order/core/store/effects/order-return-request.effect.spec.ts +++ b/feature-libs/order/core/store/effects/order-return-request.effect.spec.ts @@ -82,7 +82,7 @@ describe('Order Return Request effect', () => { it('should handle failures for create order return request', () => { spyOn(orderHistoryConnector, 'return').and.returnValue( - throwError('Error') + throwError(() => 'Error') ); const action = new OrderActions.CreateOrderReturnRequest({ @@ -127,7 +127,7 @@ describe('Order Return Request effect', () => { it('should handle failures for load return request list', () => { spyOn(orderHistoryConnector, 'getReturnRequestList').and.returnValue( - throwError('Error') + throwError(() => 'Error') ); const action = new OrderActions.LoadOrderReturnRequestList({ userId: 'test@sap.com', @@ -171,7 +171,7 @@ describe('Order Return Request effect', () => { it('should handle failures for load an order return request', () => { spyOn(orderHistoryConnector, 'getReturnRequestDetail').and.returnValue( - throwError('Error') + throwError(() => 'Error') ); const action = new OrderActions.LoadOrderReturnRequest({ @@ -212,7 +212,7 @@ describe('Order Return Request effect', () => { it('should handle failures for cancel return request', () => { spyOn(orderHistoryConnector, 'cancelReturnRequest').and.returnValue( - throwError('Error') + throwError(() => 'Error') ); const action = new OrderActions.CancelOrderReturnRequest( diff --git a/feature-libs/order/core/store/effects/orders.effect.spec.ts b/feature-libs/order/core/store/effects/orders.effect.spec.ts index 60c63ccf22f..579f04514d1 100644 --- a/feature-libs/order/core/store/effects/orders.effect.spec.ts +++ b/feature-libs/order/core/store/effects/orders.effect.spec.ts @@ -75,7 +75,7 @@ describe('Orders effect', () => { it('should handle failures for load user Orders', () => { spyOn(orderHistoryConnector, 'getHistory').and.returnValue( - throwError(mockError) + throwError(() => mockError) ); const action = new OrderActions.LoadUserOrders({ @@ -121,7 +121,7 @@ describe('Orders effect', () => { spyOn( replenishmentOrderHistoryConnector, 'loadReplenishmentDetailsHistory' - ).and.returnValue(throwError(mockError)); + ).and.returnValue(throwError(() => mockError)); const action = new OrderActions.LoadUserOrders({ userId: 'test@sap.com', diff --git a/feature-libs/order/core/store/effects/replenishment-order-details.effect.spec.ts b/feature-libs/order/core/store/effects/replenishment-order-details.effect.spec.ts index cdd4587ce35..e8a67cb284b 100644 --- a/feature-libs/order/core/store/effects/replenishment-order-details.effect.spec.ts +++ b/feature-libs/order/core/store/effects/replenishment-order-details.effect.spec.ts @@ -95,7 +95,7 @@ describe('ReplenishmentOrderDetailsEffect', () => { }); it('should return an error when it fails to load a replenishment order details', () => { - spyOn(connector, 'load').and.returnValue(throwError(mockError)); + spyOn(connector, 'load').and.returnValue(throwError(() => mockError)); const action = new OrderActions.LoadReplenishmentOrderDetails({ userId: mockUserId, @@ -138,7 +138,7 @@ describe('ReplenishmentOrderDetailsEffect', () => { it('should return an error when it fails to cancel a replenishment order', () => { spyOn(connector, 'cancelReplenishmentOrder').and.returnValue( - throwError(mockError) + throwError(() => mockError) ); const action = new OrderActions.CancelReplenishmentOrder({ diff --git a/feature-libs/order/core/store/effects/replenishment-orders.effect.spec.ts b/feature-libs/order/core/store/effects/replenishment-orders.effect.spec.ts index bfd168058ab..eea77e2a91b 100644 --- a/feature-libs/order/core/store/effects/replenishment-orders.effect.spec.ts +++ b/feature-libs/order/core/store/effects/replenishment-orders.effect.spec.ts @@ -67,7 +67,7 @@ describe('Replenishment Orders effect', () => { it('should handle failures for load user Replenishment Orders', () => { spyOn(replenishmentOrderHistoryConnector, 'loadHistory').and.returnValue( - throwError('Error') + throwError(() => 'Error') ); const action = new OrderActions.LoadUserReplenishmentOrders({ diff --git a/feature-libs/order/occ/adapters/occ-order-history.adapter.ts b/feature-libs/order/occ/adapters/occ-order-history.adapter.ts index d2972ee8344..a3258b33c3d 100644 --- a/feature-libs/order/occ/adapters/occ-order-history.adapter.ts +++ b/feature-libs/order/occ/adapters/occ-order-history.adapter.ts @@ -9,30 +9,31 @@ import { Injectable } from '@angular/core'; import { ConverterService, InterceptorUtil, - Occ, - OccEndpointsService, OCC_USER_ID_ANONYMOUS, OCC_USER_ID_CURRENT, + Occ, + OccEndpointsService, USE_CLIENT_TOKEN, + normalizeHttpError, } from '@spartacus/core'; import { OrderHistoryAdapter } from '@spartacus/order/core'; import { + CONSIGNMENT_TRACKING_NORMALIZER, CancellationRequestEntryInputList, ConsignmentTracking, - CONSIGNMENT_TRACKING_NORMALIZER, - Order, - OrderHistoryList, ORDER_HISTORY_NORMALIZER, ORDER_NORMALIZER, ORDER_RETURNS_NORMALIZER, ORDER_RETURN_REQUEST_INPUT_SERIALIZER, ORDER_RETURN_REQUEST_NORMALIZER, + Order, + OrderHistoryList, ReturnRequest, ReturnRequestEntryInputList, ReturnRequestList, ReturnRequestModification, } from '@spartacus/order/root'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; const CONTENT_TYPE_JSON_HEADER = { 'Content-Type': 'application/json' }; @@ -112,9 +113,11 @@ export class OccOrderHistoryAdapter implements OrderHistoryAdapter { ...CONTENT_TYPE_JSON_HEADER, }); - return this.http - .post(url, cancelRequestInput, { headers }) - .pipe(catchError((error: any) => throwError(error))); + return this.http.post(url, cancelRequestInput, { headers }).pipe( + catchError((error: any) => { + throw normalizeHttpError(error); + }) + ); } public createReturnRequest( @@ -134,7 +137,9 @@ export class OccOrderHistoryAdapter implements OrderHistoryAdapter { ); return this.http.post(url, returnRequestInput, { headers }).pipe( - catchError((error: any) => throwError(error)), + catchError((error: any) => { + throw normalizeHttpError(error); + }), this.converter.pipeable(ORDER_RETURN_REQUEST_NORMALIZER) ); } @@ -191,8 +196,10 @@ export class OccOrderHistoryAdapter implements OrderHistoryAdapter { ...CONTENT_TYPE_JSON_HEADER, }); - return this.http - .patch(url, returnRequestModification, { headers }) - .pipe(catchError((error: any) => throwError(error))); + return this.http.patch(url, returnRequestModification, { headers }).pipe( + catchError((error: any) => { + throw normalizeHttpError(error); + }) + ); } } diff --git a/feature-libs/order/occ/adapters/occ-order.adapter.spec.ts b/feature-libs/order/occ/adapters/occ-order.adapter.spec.ts index 91a473d1e49..6d3b04ac8c1 100644 --- a/feature-libs/order/occ/adapters/occ-order.adapter.spec.ts +++ b/feature-libs/order/occ/adapters/occ-order.adapter.spec.ts @@ -122,7 +122,9 @@ describe('OccOrderAdapter', () => { describe(`back-off`, () => { it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { - spyOn(httpClient, 'post').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'post').and.returnValue( + throwError(() => mockJaloError) + ); let result: HttpErrorModel | undefined; const subscription = service @@ -146,7 +148,7 @@ describe('OccOrderAdapter', () => { if (calledTimes === 3) { return of({}); } - return throwError(mockJaloError); + return throwError(() => mockJaloError); }) ); diff --git a/feature-libs/order/occ/adapters/occ-order.adapter.ts b/feature-libs/order/occ/adapters/occ-order.adapter.ts index 07a13e370e2..2ba45abdf0c 100644 --- a/feature-libs/order/occ/adapters/occ-order.adapter.ts +++ b/feature-libs/order/occ/adapters/occ-order.adapter.ts @@ -20,7 +20,7 @@ import { } from '@spartacus/core'; import { OrderAdapter } from '@spartacus/order/core'; import { ORDER_NORMALIZER, Order } from '@spartacus/order/root'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; @Injectable() @@ -53,9 +53,9 @@ export class OccOrderAdapter implements OrderAdapter { { headers } ) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), backOff({ shouldRetry: isJaloError, }), diff --git a/feature-libs/order/occ/adapters/occ-reorder-order.adapter.ts b/feature-libs/order/occ/adapters/occ-reorder-order.adapter.ts index 51625589b5f..3253c07c68c 100644 --- a/feature-libs/order/occ/adapters/occ-reorder-order.adapter.ts +++ b/feature-libs/order/occ/adapters/occ-reorder-order.adapter.ts @@ -15,7 +15,7 @@ import { } from '@spartacus/core'; import { ReorderOrderAdapter } from '@spartacus/order/core'; import { REORDER_ORDER_NORMALIZER } from '@spartacus/order/root'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; @Injectable() @@ -34,9 +34,9 @@ export class OccReorderOrderAdapter implements ReorderOrderAdapter { return this.http .post(this.getReorderOrderEndpoint(orderId, userId), {}, { headers }) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), this.converter.pipeable(REORDER_ORDER_NORMALIZER) ); } diff --git a/feature-libs/order/occ/adapters/occ-scheduled-replenishment-order.adapter.spec.ts b/feature-libs/order/occ/adapters/occ-scheduled-replenishment-order.adapter.spec.ts index 1d74c2afc94..024f3355e8b 100644 --- a/feature-libs/order/occ/adapters/occ-scheduled-replenishment-order.adapter.spec.ts +++ b/feature-libs/order/occ/adapters/occ-scheduled-replenishment-order.adapter.spec.ts @@ -127,7 +127,9 @@ describe(`OccScheduledReplenishmentOrderAdapter`, () => { describe(`back-off`, () => { it(`should unsuccessfully backOff on Jalo error`, fakeAsync(() => { - spyOn(httpClient, 'post').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'post').and.returnValue( + throwError(() => mockJaloError) + ); let result: HttpErrorModel | undefined; const subscription = occAdapter @@ -156,7 +158,7 @@ describe(`OccScheduledReplenishmentOrderAdapter`, () => { if (calledTimes === 3) { return of(mockReplenishmentOrder); } - return throwError(mockJaloError); + return throwError(() => mockJaloError); }) ); diff --git a/feature-libs/order/occ/adapters/occ-scheduled-replenishment-order.adapter.ts b/feature-libs/order/occ/adapters/occ-scheduled-replenishment-order.adapter.ts index 539e171cc5a..f9fdf4456e6 100644 --- a/feature-libs/order/occ/adapters/occ-scheduled-replenishment-order.adapter.ts +++ b/feature-libs/order/occ/adapters/occ-scheduled-replenishment-order.adapter.ts @@ -21,7 +21,7 @@ import { ReplenishmentOrder, ScheduleReplenishmentForm, } from '@spartacus/order/root'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; @Injectable() @@ -60,9 +60,9 @@ export class OccScheduledReplenishmentOrderAdapter { headers } ) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), backOff({ shouldRetry: isJaloError }), this.converter.pipeable(REPLENISHMENT_ORDER_NORMALIZER) ); diff --git a/feature-libs/order/package.json b/feature-libs/order/package.json index 9f1366dbbb3..b2ca2d07e4a 100644 --- a/feature-libs/order/package.json +++ b/feature-libs/order/package.json @@ -40,7 +40,7 @@ "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", "@spartacus/user": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/feature-libs/organization/account-summary/core/account-summary-page-meta.resolver.spec.ts b/feature-libs/organization/account-summary/core/account-summary-page-meta.resolver.spec.ts index 7db9dfdc47e..ab3310d4d6e 100644 --- a/feature-libs/organization/account-summary/core/account-summary-page-meta.resolver.spec.ts +++ b/feature-libs/organization/account-summary/core/account-summary-page-meta.resolver.spec.ts @@ -8,8 +8,7 @@ import { RoutingService, SemanticPathService, } from '@spartacus/core'; -import { Observable, of } from 'rxjs'; -import { take } from 'rxjs/operators'; +import { firstValueFrom, Observable, of } from 'rxjs'; import { AccountSummaryPageMetaResolver } from './account-summary-page-meta.resolver'; class MockRoutingService { @@ -78,7 +77,7 @@ describe('AccountSummaryPageMetaResolver', () => { describe('resolveTitle', () => { it('should emit title of CMS content page ', async () => { - expect(await service.resolveTitle().pipe(take(1)).toPromise()).toBe( + expect(await firstValueFrom(service.resolveTitle())).toBe( 'testContentPageTitle' ); }); @@ -92,7 +91,7 @@ describe('AccountSummaryPageMetaResolver', () => { ); }); it('should NOT return breadcrumb for the Account Summary page', async () => { - let val = await service.resolveBreadcrumbs().pipe(take(1)).toPromise(); + let val = await firstValueFrom(service.resolveBreadcrumbs()); expect(val).toEqual([testHomeBreadcrumb, organizationBreadcrumb]); }); }); @@ -114,7 +113,7 @@ describe('AccountSummaryPageMetaResolver', () => { }); it('should insert breadcrumb for the Account Summary page right after the Homepage breadcrumb', async () => { - let val = await service.resolveBreadcrumbs().pipe(take(1)).toPromise(); + let val = await firstValueFrom(service.resolveBreadcrumbs()); expect(val).toEqual([ testHomeBreadcrumb, organizationBreadcrumb, diff --git a/feature-libs/organization/account-summary/occ/adapters/occ-account-summary.adapter.ts b/feature-libs/organization/account-summary/occ/adapters/occ-account-summary.adapter.ts index fa811f50ab8..9d0e0cf6637 100644 --- a/feature-libs/organization/account-summary/occ/adapters/occ-account-summary.adapter.ts +++ b/feature-libs/organization/account-summary/occ/adapters/occ-account-summary.adapter.ts @@ -22,7 +22,7 @@ import { AccountSummaryList, DocumentQueryParams, } from '@spartacus/organization/account-summary/root'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; @Injectable() @@ -44,9 +44,9 @@ export class OccAccountSummaryAdapter implements AccountSummaryAdapter { this.buildAccountSummaryUrl(userId, orgUnitId) ) .pipe( - catchError((error: HttpErrorResponse) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error: HttpErrorResponse) => { + throw normalizeHttpError(error, this.logger); + }), this.converter.pipeable(ACCOUNT_SUMMARY_NORMALIZER) ); } @@ -61,9 +61,9 @@ export class OccAccountSummaryAdapter implements AccountSummaryAdapter { this.buildDocumentListUrl(userId, orgUnitId, params) ) .pipe( - catchError((error: HttpErrorResponse) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error: HttpErrorResponse) => { + throw normalizeHttpError(error, this.logger); + }), this.converter.pipeable(ACCOUNT_SUMMARY_DOCUMENT_NORMALIZER) ); } @@ -89,9 +89,9 @@ export class OccAccountSummaryAdapter implements AccountSummaryAdapter { options ) .pipe( - catchError((error: HttpErrorResponse) => - throwError(normalizeHttpError(error, this.logger)) - ) + catchError((error: HttpErrorResponse) => { + throw normalizeHttpError(error, this.logger); + }) ); } diff --git a/feature-libs/organization/account-summary/root/http-interceptors/blob-error.interceptor.ts b/feature-libs/organization/account-summary/root/http-interceptors/blob-error.interceptor.ts index 1c41703b52e..5624193ab4f 100644 --- a/feature-libs/organization/account-summary/root/http-interceptors/blob-error.interceptor.ts +++ b/feature-libs/organization/account-summary/root/http-interceptors/blob-error.interceptor.ts @@ -13,7 +13,7 @@ import { } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { FileReaderService } from '@spartacus/storefront'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError, switchMap } from 'rxjs/operators'; @Injectable({ @@ -38,17 +38,15 @@ export class BlobErrorInterceptor implements HttpInterceptor { .pipe( switchMap((errorString: any) => { const error = JSON.parse(errorString); - return throwError( - new HttpErrorResponse({ - ...errResponse, - error, - url: errResponse.url ?? undefined, - }) - ); + throw new HttpErrorResponse({ + ...errResponse, + error, + url: errResponse.url ?? undefined, + }); }) ); } else { - return throwError(errResponse); + throw errResponse; } }) ); diff --git a/feature-libs/organization/administration/components/budget/services/budget-route-page-meta.resolver.spec.ts b/feature-libs/organization/administration/components/budget/services/budget-route-page-meta.resolver.spec.ts index 0141223d5e8..135bfcbd1de 100644 --- a/feature-libs/organization/administration/components/budget/services/budget-route-page-meta.resolver.spec.ts +++ b/feature-libs/organization/administration/components/budget/services/budget-route-page-meta.resolver.spec.ts @@ -1,8 +1,7 @@ import { TestBed } from '@angular/core/testing'; import { I18nTestingModule } from '@spartacus/core'; import { Budget } from '@spartacus/organization/administration/core'; -import { Observable, of } from 'rxjs'; -import { take } from 'rxjs/operators'; +import { firstValueFrom, Observable, of } from 'rxjs'; import { BudgetRoutePageMetaResolver } from './budget-route-page-meta.resolver'; import { CurrentBudgetService } from './current-budget.service'; @@ -29,13 +28,12 @@ describe('BudgetRoutePageMetaResolver', () => { it('should emit breadcrumb with translated i18n key, using current item as params', async () => { expect( - await resolver - .resolveBreadcrumbs({ + await firstValueFrom( + resolver.resolveBreadcrumbs({ url: 'testPath', pageMetaConfig: { breadcrumb: { i18n: 'testTranslation' } }, }) - .pipe(take(1)) - .toPromise() + ) ).toEqual([{ label: 'testTranslation code:testCode', link: 'testPath' }]); }); }); diff --git a/feature-libs/organization/administration/components/cost-center/services/cost-center-route-page-meta.resolver.spec.ts b/feature-libs/organization/administration/components/cost-center/services/cost-center-route-page-meta.resolver.spec.ts index 0e18bbdc7fa..f264ca3a9be 100644 --- a/feature-libs/organization/administration/components/cost-center/services/cost-center-route-page-meta.resolver.spec.ts +++ b/feature-libs/organization/administration/components/cost-center/services/cost-center-route-page-meta.resolver.spec.ts @@ -1,7 +1,6 @@ import { TestBed } from '@angular/core/testing'; import { CostCenter, I18nTestingModule } from '@spartacus/core'; -import { Observable, of } from 'rxjs'; -import { take } from 'rxjs/operators'; +import { firstValueFrom, Observable, of } from 'rxjs'; import { CostCenterRoutePageMetaResolver } from './cost-center-route-page-meta.resolver'; import { CurrentCostCenterService } from './current-cost-center.service'; @@ -28,13 +27,12 @@ describe('CostCenterRouteBreadcrumbResolver', () => { it('should emit breadcrumb with translated i18n key, using current item as params', async () => { expect( - await resolver - .resolveBreadcrumbs({ + await firstValueFrom( + resolver.resolveBreadcrumbs({ url: 'testPath', pageMetaConfig: { breadcrumb: { i18n: 'testTranslation' } }, }) - .pipe(take(1)) - .toPromise() + ) ).toEqual([{ label: 'testTranslation code:testCode', link: 'testPath' }]); }); }); diff --git a/feature-libs/organization/administration/components/permission/services/permission-route-page-meta.resolver.spec.ts b/feature-libs/organization/administration/components/permission/services/permission-route-page-meta.resolver.spec.ts index a5e1eaab258..32a8140f4d0 100644 --- a/feature-libs/organization/administration/components/permission/services/permission-route-page-meta.resolver.spec.ts +++ b/feature-libs/organization/administration/components/permission/services/permission-route-page-meta.resolver.spec.ts @@ -1,8 +1,7 @@ import { TestBed } from '@angular/core/testing'; import { I18nTestingModule } from '@spartacus/core'; import { Permission } from '@spartacus/organization/administration/core'; -import { BehaviorSubject, Observable } from 'rxjs'; -import { take } from 'rxjs/operators'; +import { BehaviorSubject, firstValueFrom, Observable } from 'rxjs'; import { CurrentPermissionService } from './current-permission.service'; import { PermissionRoutePageMetaResolver } from './permission-route-page-meta.resolver'; @@ -33,26 +32,24 @@ describe('PermissionRoutePageMetaResolver', () => { it('should emit breadcrumb with translated i18n key, using current item as params', async () => { currentItem$.next({ code: 'testCode' }); expect( - await resolver - .resolveBreadcrumbs({ + await firstValueFrom( + resolver.resolveBreadcrumbs({ url: 'testPath', pageMetaConfig: { breadcrumb: { i18n: 'testTranslation' } }, }) - .pipe(take(1)) - .toPromise() + ) ).toEqual([{ label: 'testTranslation code:testCode', link: 'testPath' }]); }); it('should emit breadcrumb with translated i18n key, using {} as params', async () => { currentItem$.next(undefined); expect( - await resolver - .resolveBreadcrumbs({ + await firstValueFrom( + resolver.resolveBreadcrumbs({ url: 'testPath', pageMetaConfig: { breadcrumb: { i18n: 'testTranslation' } }, }) - .pipe(take(1)) - .toPromise() + ) ).toEqual([{ label: 'testTranslation', link: 'testPath' }]); }); }); diff --git a/feature-libs/organization/administration/components/unit/services/unit-address-route-page-meta.resolver.spec.ts b/feature-libs/organization/administration/components/unit/services/unit-address-route-page-meta.resolver.spec.ts index 82a4e18da1c..4477f57fb42 100644 --- a/feature-libs/organization/administration/components/unit/services/unit-address-route-page-meta.resolver.spec.ts +++ b/feature-libs/organization/administration/components/unit/services/unit-address-route-page-meta.resolver.spec.ts @@ -1,7 +1,6 @@ import { TestBed } from '@angular/core/testing'; import { Address, I18nTestingModule } from '@spartacus/core'; -import { Observable, of } from 'rxjs'; -import { take } from 'rxjs/operators'; +import { firstValueFrom, Observable, of } from 'rxjs'; import { CurrentUnitAddressService } from '../links/addresses/services/current-unit-address.service'; import { UnitAddressRoutePageMetaResolver } from './unit-address-route-page-meta.resolver'; @@ -28,13 +27,12 @@ describe('UnitAddressRoutePageMetaResolver', () => { it('should emit breadcrumb with translated i18n key, using current item as params', async () => { expect( - await resolver - .resolveBreadcrumbs({ + await firstValueFrom( + resolver.resolveBreadcrumbs({ url: 'testPath', pageMetaConfig: { breadcrumb: { i18n: 'testTranslation' } }, }) - .pipe(take(1)) - .toPromise() + ) ).toEqual([ { label: 'testTranslation formattedAddress:testAddress', diff --git a/feature-libs/organization/administration/components/unit/services/unit-route-page-meta.resolver.spec.ts b/feature-libs/organization/administration/components/unit/services/unit-route-page-meta.resolver.spec.ts index f19ed9ad8fa..cd34609352f 100644 --- a/feature-libs/organization/administration/components/unit/services/unit-route-page-meta.resolver.spec.ts +++ b/feature-libs/organization/administration/components/unit/services/unit-route-page-meta.resolver.spec.ts @@ -1,7 +1,6 @@ import { TestBed } from '@angular/core/testing'; import { B2BUnit, I18nTestingModule } from '@spartacus/core'; -import { Observable, of } from 'rxjs'; -import { take } from 'rxjs/operators'; +import { firstValueFrom, Observable, of } from 'rxjs'; import { CurrentUnitService } from './current-unit.service'; import { UnitRoutePageMetaResolver } from './unit-route-page-meta.resolver'; @@ -28,13 +27,12 @@ describe('UnitRoutePageMetaResolver', () => { it('should emit breadcrumb with translated i18n key, using current item as params', async () => { expect( - await resolver - .resolveBreadcrumbs({ + await firstValueFrom( + resolver.resolveBreadcrumbs({ url: 'testPath', pageMetaConfig: { breadcrumb: { i18n: 'testTranslation' } }, }) - .pipe(take(1)) - .toPromise() + ) ).toEqual([{ label: 'testTranslation name:testName', link: 'testPath' }]); }); }); diff --git a/feature-libs/organization/administration/components/user-group/services/user-group-page-meta.resolver.spec.ts b/feature-libs/organization/administration/components/user-group/services/user-group-page-meta.resolver.spec.ts index 7ec487a0ee6..b4abf81880f 100644 --- a/feature-libs/organization/administration/components/user-group/services/user-group-page-meta.resolver.spec.ts +++ b/feature-libs/organization/administration/components/user-group/services/user-group-page-meta.resolver.spec.ts @@ -1,8 +1,7 @@ import { TestBed } from '@angular/core/testing'; import { I18nTestingModule } from '@spartacus/core'; import { UserGroup } from '@spartacus/organization/administration/core'; -import { Observable, of } from 'rxjs'; -import { take } from 'rxjs/operators'; +import { firstValueFrom, Observable, of } from 'rxjs'; import { CurrentUserGroupService } from './current-user-group.service'; import { UserGroupRoutePageMetaResolver } from './user-group-route-page-meta.resolver'; @@ -29,13 +28,12 @@ describe('UserGroupRoutePageMetaResolver', () => { it('should emit breadcrumb with translated i18n key, using current item as params', async () => { expect( - await resolver - .resolveBreadcrumbs({ + await firstValueFrom( + resolver.resolveBreadcrumbs({ url: 'testPath', pageMetaConfig: { breadcrumb: { i18n: 'testTranslation' } }, }) - .pipe(take(1)) - .toPromise() + ) ).toEqual([{ label: 'testTranslation name:testName', link: 'testPath' }]); }); }); diff --git a/feature-libs/organization/administration/components/user/services/user-route-page-meta.resolver.spec.ts b/feature-libs/organization/administration/components/user/services/user-route-page-meta.resolver.spec.ts index e7e3e262413..9580f2f55c8 100644 --- a/feature-libs/organization/administration/components/user/services/user-route-page-meta.resolver.spec.ts +++ b/feature-libs/organization/administration/components/user/services/user-route-page-meta.resolver.spec.ts @@ -1,7 +1,6 @@ import { TestBed } from '@angular/core/testing'; import { B2BUser, I18nTestingModule } from '@spartacus/core'; -import { Observable, of } from 'rxjs'; -import { take } from 'rxjs/operators'; +import { firstValueFrom, Observable, of } from 'rxjs'; import { CurrentUserService } from './current-user.service'; import { UserRoutePageMetaResolver } from './user-route-page-meta.resolver'; @@ -28,13 +27,12 @@ describe('UserRoutePageMetaResolver', () => { it('should emit breadcrumb with translated i18n key, using current item as params', async () => { expect( - await resolver - .resolveBreadcrumbs({ + await firstValueFrom( + resolver.resolveBreadcrumbs({ url: 'testPath', pageMetaConfig: { breadcrumb: { i18n: 'testTranslation' } }, }) - .pipe(take(1)) - .toPromise() + ) ).toEqual([{ label: 'testTranslation name:testName', link: 'testPath' }]); }); }); diff --git a/feature-libs/organization/administration/core/services/organization-page-meta.resolver.spec.ts b/feature-libs/organization/administration/core/services/organization-page-meta.resolver.spec.ts index 4c88ad92360..eb57fda678a 100644 --- a/feature-libs/organization/administration/core/services/organization-page-meta.resolver.spec.ts +++ b/feature-libs/organization/administration/core/services/organization-page-meta.resolver.spec.ts @@ -8,8 +8,7 @@ import { RoutingService, SemanticPathService, } from '@spartacus/core'; -import { Observable, of } from 'rxjs'; -import { take } from 'rxjs/operators'; +import { firstValueFrom, Observable, of } from 'rxjs'; import { OrganizationPageMetaResolver } from './organization-page-meta.resolver'; class MockRoutingService { @@ -74,7 +73,7 @@ describe('OrganizationPageMetaResolver', () => { describe('resolveTitle', () => { it('should emit title of CMS content page ', async () => { - expect(await service.resolveTitle().pipe(take(1)).toPromise()).toBe( + expect(await firstValueFrom(service.resolveTitle())).toBe( 'testContentPageTitle' ); }); @@ -89,9 +88,9 @@ describe('OrganizationPageMetaResolver', () => { }); it('should NOT return breadcrumb for the Organization page', async () => { - expect( - await service.resolveBreadcrumbs().pipe(take(1)).toPromise() - ).toEqual([testHomeBreadcrumb]); + expect(await firstValueFrom(service.resolveBreadcrumbs())).toEqual([ + testHomeBreadcrumb, + ]); }); }); @@ -112,9 +111,7 @@ describe('OrganizationPageMetaResolver', () => { }); it('should insert breadcrumb for the Organization page right after the Homepage breadcrumb', async () => { - expect( - await service.resolveBreadcrumbs().pipe(take(1)).toPromise() - ).toEqual([ + expect(await firstValueFrom(service.resolveBreadcrumbs())).toEqual([ testHomeBreadcrumb, organizationBreadcrumb, testBudgetsBreadcrumb, diff --git a/feature-libs/organization/administration/core/store/effects/b2b-user.effect.spec.ts b/feature-libs/organization/administration/core/store/effects/b2b-user.effect.spec.ts index 41713dfbb51..4362c574bd0 100644 --- a/feature-libs/organization/administration/core/store/effects/b2b-user.effect.spec.ts +++ b/feature-libs/organization/administration/core/store/effects/b2b-user.effect.spec.ts @@ -194,7 +194,7 @@ describe('B2B User Effects', () => { it('should return LoadB2BUserFail action if user not loaded', () => { b2bUserConnector.get = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new B2BUserActions.LoadB2BUser({ userId, orgCustomerId }); const completion = new B2BUserActions.LoadB2BUserFail({ @@ -226,7 +226,7 @@ describe('B2B User Effects', () => { it('should return LoadB2BUsersFail action if B2B Users not loaded', () => { b2bUserConnector.getList = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new B2BUserActions.LoadB2BUsers({ userId, params }); const completion = new B2BUserActions.LoadB2BUsersFail({ error, params }); @@ -264,7 +264,7 @@ describe('B2B User Effects', () => { it('should return LoadB2BUserUserGroupsFail action if B2BUser UserGroup not loaded', () => { b2bUserConnector.getUserGroups = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new B2BUserActions.LoadB2BUserUserGroups({ userId, @@ -309,7 +309,7 @@ describe('B2B User Effects', () => { it('should return CreateB2BUserFail action if user not created', () => { b2bUserConnector.create = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new B2BUserActions.CreateB2BUser({ userId, orgCustomer }); const completion1 = new B2BUserActions.CreateB2BUserFail({ @@ -348,7 +348,7 @@ describe('B2B User Effects', () => { it('should return UpdateB2BUserFail action if user not updated', () => { b2bUserConnector.update = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new B2BUserActions.UpdateB2BUser({ userId, @@ -419,7 +419,7 @@ describe('B2B User Effects', () => { it('should return LoadB2BUserApproversFail action if approvers not loaded', () => { b2bUserConnector.getApprovers = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new B2BUserActions.LoadB2BUserApprovers({ userId, @@ -471,7 +471,7 @@ describe('B2B User Effects', () => { it('should return LoadB2BUserApproversFail action if Permissions not loaded', () => { b2bUserConnector.getPermissions = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new B2BUserActions.LoadB2BUserPermissions({ userId, @@ -519,7 +519,7 @@ describe('B2B User Effects', () => { it('should return AssignB2BUserApproverFail action if approver not assigned', () => { b2bUserConnector.assignApprover = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new B2BUserActions.AssignB2BUserApprover({ userId, @@ -568,7 +568,7 @@ describe('B2B User Effects', () => { it('should return UnassignB2BUserApproverFail action if approver not unassigned', () => { b2bUserConnector.unassignApprover = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new B2BUserActions.UnassignB2BUserApprover({ userId, @@ -618,7 +618,7 @@ describe('B2B User Effects', () => { it('should return AssignB2BUserPermissionFail action if permission not assigned', () => { b2bUserConnector.assignPermission = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new B2BUserActions.AssignB2BUserPermission({ userId, @@ -668,7 +668,7 @@ describe('B2B User Effects', () => { it('should return UnassignB2BUserPermissionFail action if permission not unassigned', () => { b2bUserConnector.unassignPermission = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new B2BUserActions.UnassignB2BUserPermission({ userId, @@ -718,7 +718,7 @@ describe('B2B User Effects', () => { it('should return AssignB2BUserUserGroupFail action if UserGroup was not assigned', () => { b2bUserConnector.assignUserGroup = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new B2BUserActions.AssignB2BUserUserGroup({ userId, @@ -768,7 +768,7 @@ describe('B2B User Effects', () => { it('should return UnassignB2BUserUserGroupFail action if UserGroup was not unassigned', () => { b2bUserConnector.unassignUserGroup = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new B2BUserActions.UnassignB2BUserUserGroup({ userId, diff --git a/feature-libs/organization/administration/core/store/effects/budget.effect.spec.ts b/feature-libs/organization/administration/core/store/effects/budget.effect.spec.ts index bfa3576d047..59d7f1662c2 100644 --- a/feature-libs/organization/administration/core/store/effects/budget.effect.spec.ts +++ b/feature-libs/organization/administration/core/store/effects/budget.effect.spec.ts @@ -106,7 +106,7 @@ describe('Budget Effects', () => { it('should return LoadBudgetFail action if budget not updated', () => { budgetConnector.get = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new BudgetActions.LoadBudget({ userId, budgetCode }); const completion = new BudgetActions.LoadBudgetFail({ @@ -140,7 +140,7 @@ describe('Budget Effects', () => { it('should return LoadBudgetsFail action if budgets not loaded', () => { budgetConnector.getList = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new BudgetActions.LoadBudgets({ userId, params }); const completion = new BudgetActions.LoadBudgetsFail({ error, params }); @@ -166,7 +166,7 @@ describe('Budget Effects', () => { it('should return CreateBudgetFail action if budget not created', () => { budgetConnector.create = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new BudgetActions.CreateBudget({ userId, budget }); const completion1 = new BudgetActions.CreateBudgetFail({ @@ -204,7 +204,7 @@ describe('Budget Effects', () => { it('should return UpdateBudgetFail action if budget not created', () => { budgetConnector.update = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new BudgetActions.UpdateBudget({ userId, diff --git a/feature-libs/organization/administration/core/store/effects/cost-center.effect.spec.ts b/feature-libs/organization/administration/core/store/effects/cost-center.effect.spec.ts index 0298c619ba0..2ea04303d3d 100644 --- a/feature-libs/organization/administration/core/store/effects/cost-center.effect.spec.ts +++ b/feature-libs/organization/administration/core/store/effects/cost-center.effect.spec.ts @@ -133,7 +133,7 @@ describe('CostCenter Effects', () => { it('should return LoadCostCenterFail action if costCenter not updated', () => { costCenterConnector.get = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new CostCenterActions.LoadCostCenter({ userId, @@ -175,7 +175,7 @@ describe('CostCenter Effects', () => { it('should return LoadCostCentersFail action if costCenters not loaded', () => { costCenterConnector.getList = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new CostCenterActions.LoadCostCenters({ userId, params }); const completion = new CostCenterActions.LoadCostCentersFail({ @@ -212,7 +212,7 @@ describe('CostCenter Effects', () => { it('should return CreateCostCenterFail action if costCenter not created', () => { costCenterConnector.create = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new CostCenterActions.CreateCostCenter({ userId, @@ -258,7 +258,7 @@ describe('CostCenter Effects', () => { it('should return UpdateCostCenterFail action if costCenter not created', () => { costCenterConnector.update = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new CostCenterActions.UpdateCostCenter({ userId, @@ -310,7 +310,7 @@ describe('CostCenter Effects', () => { it('should return LoadAssignedBudgetsFail action if budgets not loaded', () => { costCenterConnector.getBudgets = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new CostCenterActions.LoadAssignedBudgets({ userId, @@ -359,7 +359,7 @@ describe('CostCenter Effects', () => { it('should return UpdateCostCenterFail action if budget not assigned', () => { costCenterConnector.assignBudget = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new CostCenterActions.AssignBudget({ userId, @@ -407,7 +407,7 @@ describe('CostCenter Effects', () => { it('should return UnassignBudgetFail action if budget not unassigned', () => { costCenterConnector.unassignBudget = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new CostCenterActions.UnassignBudget({ userId, diff --git a/feature-libs/organization/administration/core/store/effects/org-unit.effect.spec.ts b/feature-libs/organization/administration/core/store/effects/org-unit.effect.spec.ts index 83d16efabf1..b3597452250 100644 --- a/feature-libs/organization/administration/core/store/effects/org-unit.effect.spec.ts +++ b/feature-libs/organization/administration/core/store/effects/org-unit.effect.spec.ts @@ -137,7 +137,7 @@ describe('OrgUnit Effects', () => { it('should return LoadOrgUnitFail action if orgUnit not updated', () => { orgUnitConnector.get = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new OrgUnitActions.LoadOrgUnit({ userId, orgUnitId }); const completion = new OrgUnitActions.LoadOrgUnitFail({ @@ -167,7 +167,7 @@ describe('OrgUnit Effects', () => { it('should return LoadOrgUnitNodesFail action if orgUnits not loaded', () => { orgUnitConnector.getList = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new OrgUnitActions.LoadOrgUnitNodes({ userId }); const completion = new OrgUnitActions.LoadOrgUnitNodesFail({ error }); @@ -193,7 +193,7 @@ describe('OrgUnit Effects', () => { it('should return LoadOrgUnitNodesFail action if orgUnits not loaded', () => { orgUnitConnector.create = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new OrgUnitActions.CreateUnit({ userId, unit: orgUnit }); const completion1 = new OrgUnitActions.CreateUnitFail({ @@ -231,7 +231,7 @@ describe('OrgUnit Effects', () => { it('should return UpdateOrgUnitNodesFail action if orgUnits not loaded', () => { orgUnitConnector.update = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new OrgUnitActions.UpdateUnit({ userId, @@ -284,7 +284,7 @@ describe('OrgUnit Effects', () => { it('should return CreateAddressFail action if address is not loaded', () => { orgUnitConnector.createAddress = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new OrgUnitActions.CreateAddress({ userId, @@ -334,7 +334,7 @@ describe('OrgUnit Effects', () => { it('should return UpdateAddressFail action if address is not loaded', () => { orgUnitConnector.updateAddress = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new OrgUnitActions.UpdateAddress({ userId, @@ -382,7 +382,7 @@ describe('OrgUnit Effects', () => { it('should return DeleteAddressFail action if address is not loaded', () => { orgUnitConnector.deleteAddress = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new OrgUnitActions.DeleteAddress({ userId, @@ -431,7 +431,7 @@ describe('OrgUnit Effects', () => { it('should return AssignRoleFail action if address is not loaded', () => { orgUnitConnector.assignRole = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new OrgUnitActions.AssignRole({ userId, @@ -479,7 +479,7 @@ describe('OrgUnit Effects', () => { it('should return UnassignRoleFail action if address is not loaded', () => { orgUnitConnector.unassignRole = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new OrgUnitActions.UnassignRole({ userId, @@ -530,7 +530,7 @@ describe('OrgUnit Effects', () => { it('should return AssignApproverFail action if address is not loaded', () => { orgUnitConnector.assignApprover = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new OrgUnitActions.AssignApprover({ userId, @@ -584,7 +584,7 @@ describe('OrgUnit Effects', () => { it('should return UnassignApproverFail action if address is not loaded', () => { orgUnitConnector.unassignApprover = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new OrgUnitActions.UnassignApprover({ userId, @@ -629,7 +629,7 @@ describe('OrgUnit Effects', () => { it('should return LoadApprovalProcessesFail action if address is not loaded', () => { orgUnitConnector.getApprovalProcesses = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new OrgUnitActions.LoadApprovalProcesses({ userId, @@ -683,7 +683,7 @@ describe('OrgUnit Effects', () => { it('should return LoadUsersFail action if address is not loaded', () => { orgUnitConnector.getUsers = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new OrgUnitActions.LoadAssignedUsers({ userId, @@ -725,7 +725,7 @@ describe('OrgUnit Effects', () => { it('should return LoadTreeFail action if address is not loaded', () => { orgUnitConnector.getTree = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new OrgUnitActions.LoadTree({ userId, diff --git a/feature-libs/organization/administration/core/store/effects/permission.effect.spec.ts b/feature-libs/organization/administration/core/store/effects/permission.effect.spec.ts index 97b059016ab..8993d81ad9e 100644 --- a/feature-libs/organization/administration/core/store/effects/permission.effect.spec.ts +++ b/feature-libs/organization/administration/core/store/effects/permission.effect.spec.ts @@ -6,8 +6,8 @@ import { StoreModule } from '@ngrx/store'; import { normalizeHttpError, OccConfig, - SearchConfig, OrderApprovalPermissionType, + SearchConfig, } from '@spartacus/core'; import { OrganizationActions, @@ -121,7 +121,7 @@ describe('Permission Effects', () => { it('should return LoadPermissionFail action if permission not updated', () => { permissionConnector.get = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new PermissionActions.LoadPermission({ userId, @@ -163,7 +163,7 @@ describe('Permission Effects', () => { it('should return LoadPermissionsFail action if permissions not loaded', () => { permissionConnector.getList = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new PermissionActions.LoadPermissions({ userId, params }); const completion = new PermissionActions.LoadPermissionsFail({ @@ -200,7 +200,7 @@ describe('Permission Effects', () => { it('should return CreatePermissionFail action if permission not created', () => { permissionConnector.create = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new PermissionActions.CreatePermission({ userId, @@ -246,7 +246,7 @@ describe('Permission Effects', () => { it('should return UpdatePermissionFail action if permission not created', () => { permissionConnector.update = createSpy('update').and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new PermissionActions.UpdatePermission({ userId, @@ -285,7 +285,7 @@ describe('Permission Effects', () => { it('should return LoadPermissionTypesFail action if permission types are not updated', () => { permissionConnector.getTypes = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new PermissionActions.LoadPermissionTypes(); const completion = new PermissionActions.LoadPermissionTypesFail({ diff --git a/feature-libs/organization/administration/core/store/effects/user-group.effect.spec.ts b/feature-libs/organization/administration/core/store/effects/user-group.effect.spec.ts index 4477d357d4e..ca31686a8be 100644 --- a/feature-libs/organization/administration/core/store/effects/user-group.effect.spec.ts +++ b/feature-libs/organization/administration/core/store/effects/user-group.effect.spec.ts @@ -146,7 +146,7 @@ describe('UserGroup Effects', () => { it('should return LoadUserGroupFail action if userGroup not updated', () => { userGroupConnector.get = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new UserGroupActions.LoadUserGroup({ userId, @@ -186,7 +186,7 @@ describe('UserGroup Effects', () => { it('should return LoadUserGroupsFail action if userGroups not loaded', () => { userGroupConnector.getList = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new UserGroupActions.LoadUserGroups({ userId, @@ -223,7 +223,7 @@ describe('UserGroup Effects', () => { it('should return CreateUserGroupFail action if userGroup not created', () => { userGroupConnector.create = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new UserGroupActions.CreateUserGroup({ userId, @@ -264,7 +264,7 @@ describe('UserGroup Effects', () => { it('should return UpdateUserGroupFail action if userGroup not created', () => { userGroupConnector.update = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new UserGroupActions.UpdateUserGroup({ userId, @@ -310,7 +310,7 @@ describe('UserGroup Effects', () => { it('should return DeleteUserGroupFail action if userGroup not created', () => { userGroupConnector.delete = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new UserGroupActions.DeleteUserGroup({ userId, @@ -362,7 +362,7 @@ describe('UserGroup Effects', () => { it('should return LoadPermissionFail action if permissions not loaded', () => { userGroupConnector.getAvailableOrderApprovalPermissions = - createSpy().and.returnValue(throwError(httpErrorResponse)); + createSpy().and.returnValue(throwError(() => httpErrorResponse)); const action = new UserGroupActions.LoadPermissions({ userId, userGroupId, @@ -408,7 +408,7 @@ describe('UserGroup Effects', () => { it('should return CreateUserGroupOrderApprovalPermissionFail action if permission not assigned', () => { userGroupConnector.assignOrderApprovalPermission = - createSpy().and.returnValue(throwError(httpErrorResponse)); + createSpy().and.returnValue(throwError(() => httpErrorResponse)); const action = new UserGroupActions.AssignPermission({ userId, userGroupId, @@ -452,7 +452,7 @@ describe('UserGroup Effects', () => { it('should return DeleteUserGroupOrderApprovalPermissionFail action if permission not unassigned', () => { userGroupConnector.unassignOrderApprovalPermission = - createSpy().and.returnValue(throwError(httpErrorResponse)); + createSpy().and.returnValue(throwError(() => httpErrorResponse)); const action = new UserGroupActions.UnassignPermission({ userId, userGroupId, @@ -504,7 +504,7 @@ describe('UserGroup Effects', () => { it('should return LoadUserGroupAvailableOrgCustomersFail action if users not loaded', () => { userGroupConnector.getAvailableOrgCustomers = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new UserGroupActions.LoadAvailableOrgCustomers({ userId, @@ -553,7 +553,7 @@ describe('UserGroup Effects', () => { it('should return CreateUserGroupOrderApprovalPermissionFail action if user not assigned', () => { userGroupConnector.assignMember = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new UserGroupActions.AssignMember({ userId, @@ -602,7 +602,7 @@ describe('UserGroup Effects', () => { it('should return DeleteUserGroupMemberSuccessFail action if users not unassigned', () => { userGroupConnector.unassignMember = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new UserGroupActions.UnassignMember({ userId, @@ -649,7 +649,7 @@ describe('UserGroup Effects', () => { it('should return DeleteUserGroupMemberSuccessFail action if users not unassigned', () => { userGroupConnector.unassignAllMembers = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new UserGroupActions.UnassignAllMembers({ userId, diff --git a/feature-libs/organization/karma.conf.js b/feature-libs/organization/karma.conf.js index 27293165aea..6b1a12eba1d 100644 --- a/feature-libs/organization/karma.conf.js +++ b/feature-libs/organization/karma.conf.js @@ -4,9 +4,8 @@ module.exports = function (config) { config.set({ basePath: '', - frameworks: ['parallel', 'jasmine', '@angular-devkit/build-angular'], + frameworks: ['jasmine', '@angular-devkit/build-angular'], plugins: [ - require('karma-parallel'), require('karma-jasmine'), require('karma-coverage'), require('karma-chrome-launcher'), @@ -14,10 +13,6 @@ module.exports = function (config) { require('@angular-devkit/build-angular/plugins/karma'), require('karma-junit-reporter'), ], - parallelOptions: { - executors: 2, - shardStrategy: 'round-robin', - }, client: { clearContext: false, // leave Jasmine Spec Runner output visible in browser jasmine: { diff --git a/feature-libs/organization/order-approval/core/store/effects/order-approval.effect.spec.ts b/feature-libs/organization/order-approval/core/store/effects/order-approval.effect.spec.ts index 13e53fc08dd..8572b2cf9b4 100644 --- a/feature-libs/organization/order-approval/core/store/effects/order-approval.effect.spec.ts +++ b/feature-libs/organization/order-approval/core/store/effects/order-approval.effect.spec.ts @@ -4,10 +4,10 @@ import { TestBed } from '@angular/core/testing'; import { provideMockActions } from '@ngrx/effects/testing'; import { StoreModule } from '@ngrx/store'; import { normalizeHttpError, OccConfig, SearchConfig } from '@spartacus/core'; -import { OrderApprovalConnector } from '../../connectors/order-approval.connector'; import { cold, hot } from 'jasmine-marbles'; import { TestColdObservable } from 'jasmine-marbles/src/test-observables'; import { Observable, of, throwError } from 'rxjs'; +import { OrderApprovalConnector } from '../../connectors/order-approval.connector'; import { OrderApproval, OrderApprovalDecision, @@ -115,7 +115,7 @@ describe('OrderApproval Effects', () => { it('should return LoadOrderApprovalFail action if orderApproval not updated', () => { orderApprovalConnector.get = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new OrderApprovalActions.LoadOrderApproval({ userId, @@ -163,7 +163,7 @@ describe('OrderApproval Effects', () => { it('should return LoadOrderApprovalsFail action if orderApprovals not loaded', () => { orderApprovalConnector.getList = createSpy().and.returnValue( - throwError(httpErrorResponse) + throwError(() => httpErrorResponse) ); const action = new OrderApprovalActions.LoadOrderApprovals({ userId, @@ -213,7 +213,7 @@ describe('OrderApproval Effects', () => { it('should return MakeDecisionFail action if decision not created', () => { orderApprovalConnector.makeDecision = createSpy( 'makeDecision' - ).and.returnValue(throwError(httpErrorResponse)); + ).and.returnValue(throwError(() => httpErrorResponse)); const action = new OrderApprovalActions.MakeDecision({ userId, orderApprovalCode, diff --git a/feature-libs/organization/package.json b/feature-libs/organization/package.json index c11c73fc8fa..ecb52dc75bd 100644 --- a/feature-libs/organization/package.json +++ b/feature-libs/organization/package.json @@ -41,7 +41,7 @@ "@spartacus/styles": "6.6.0-1", "@spartacus/user": "6.6.0-1", "bootstrap": "^4.6.2", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/feature-libs/organization/unit-order/core/services/unit-order.service.spec.ts b/feature-libs/organization/unit-order/core/services/unit-order.service.spec.ts index 012b6f16c25..ebd3eb9d770 100644 --- a/feature-libs/organization/unit-order/core/services/unit-order.service.spec.ts +++ b/feature-libs/organization/unit-order/core/services/unit-order.service.spec.ts @@ -120,7 +120,7 @@ describe('UnitOrderService', () => { it('should NOT load order list data when user is anonymous', () => { spyOn(userIdService, 'takeUserId').and.callFake(() => { - return throwError('Error'); + return throwError(() => 'Error'); }); unitOrderService.loadOrderList(10, 1, 'byDate'); diff --git a/feature-libs/organization/unit-order/core/store/effects/unit-order.effect.spec.ts b/feature-libs/organization/unit-order/core/store/effects/unit-order.effect.spec.ts index 60b614ef974..e08662e833c 100644 --- a/feature-libs/organization/unit-order/core/store/effects/unit-order.effect.spec.ts +++ b/feature-libs/organization/unit-order/core/store/effects/unit-order.effect.spec.ts @@ -71,7 +71,7 @@ describe('Orders effect', () => { it('should handle failures for load user Orders', () => { spyOn(orderHistoryConnector, 'getUnitOrderHistory').and.returnValue( - throwError(mockError) + throwError(() => mockError) ); const action = new UnitOrderActions.LoadUnitOrders({ @@ -127,7 +127,7 @@ describe('Orders effect', () => { it('should handle failures for load order details', () => { spyOn(orderHistoryConnector, 'getUnitOrderDetail').and.returnValue( - throwError('Error') + throwError(() => 'Error') ); const action = new UnitOrderActions.LoadOrderDetails( diff --git a/feature-libs/organization/user-registration/occ/adapters/occ-user-registration.adapter.ts b/feature-libs/organization/user-registration/occ/adapters/occ-user-registration.adapter.ts index c0d2a94b29c..5ecfff5b39e 100644 --- a/feature-libs/organization/user-registration/occ/adapters/occ-user-registration.adapter.ts +++ b/feature-libs/organization/user-registration/occ/adapters/occ-user-registration.adapter.ts @@ -19,7 +19,7 @@ import { UserRegistrationAdapter, } from '@spartacus/organization/user-registration/core'; import { OrganizationUserRegistration } from '@spartacus/organization/user-registration/root'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; @Injectable({ @@ -50,9 +50,9 @@ export class OccUserRegistrationAdapter implements UserRegistrationAdapter { return this.http .post(url, userData, { headers }) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ) + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }) ); } diff --git a/feature-libs/pdf-invoices/karma.conf.js b/feature-libs/pdf-invoices/karma.conf.js index b1ea719a64b..5633e7ccd7f 100644 --- a/feature-libs/pdf-invoices/karma.conf.js +++ b/feature-libs/pdf-invoices/karma.conf.js @@ -4,9 +4,8 @@ module.exports = function (config) { config.set({ basePath: '', - frameworks: ['parallel', 'jasmine', '@angular-devkit/build-angular'], + frameworks: ['jasmine', '@angular-devkit/build-angular'], plugins: [ - require('karma-parallel'), require('karma-jasmine'), require('karma-coverage'), require('karma-chrome-launcher'), diff --git a/feature-libs/pdf-invoices/package.json b/feature-libs/pdf-invoices/package.json index e7e95267126..279af6c906a 100644 --- a/feature-libs/pdf-invoices/package.json +++ b/feature-libs/pdf-invoices/package.json @@ -33,7 +33,7 @@ "@spartacus/schematics": "6.6.0-1", "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/feature-libs/pickup-in-store/core/store/effects/default-point-of-service-name.effect.spec.ts b/feature-libs/pickup-in-store/core/store/effects/default-point-of-service-name.effect.spec.ts index d99c2a9039b..17675a29e53 100644 --- a/feature-libs/pickup-in-store/core/store/effects/default-point-of-service-name.effect.spec.ts +++ b/feature-libs/pickup-in-store/core/store/effects/default-point-of-service-name.effect.spec.ts @@ -21,7 +21,7 @@ let ForceErrorInMockUserProfileFacadeGo = false; export class MockUserProfileFacade implements Partial { update(_details: User): Observable { return ForceErrorInMockUserProfileFacadeGo - ? throwError('An RX JS ERROR') + ? throwError(() => 'An RX JS ERROR') : of({}); } get(): Observable { diff --git a/feature-libs/pickup-in-store/core/store/effects/stock.effect.spec.ts b/feature-libs/pickup-in-store/core/store/effects/stock.effect.spec.ts index 844df1136ec..162ced1b41b 100644 --- a/feature-libs/pickup-in-store/core/store/effects/stock.effect.spec.ts +++ b/feature-libs/pickup-in-store/core/store/effects/stock.effect.spec.ts @@ -68,7 +68,9 @@ describe('StockEffect', () => { statusText: 'Not Found', error: 'Error', }); - spyOn(stockConnector, 'loadStockLevels').and.returnValue(throwError(error)); + spyOn(stockConnector, 'loadStockLevels').and.returnValue( + throwError(() => error) + ); const action = new StockLevel({ productCode: 'P0001', location: '' }); const actionFail = new StockLevelFail(normalizeHttpError(error)); diff --git a/feature-libs/pickup-in-store/occ/adapters/occ-pickup-location.adapter.spec.ts b/feature-libs/pickup-in-store/occ/adapters/occ-pickup-location.adapter.spec.ts index 7d9c51b1235..728b2e9ff79 100644 --- a/feature-libs/pickup-in-store/occ/adapters/occ-pickup-location.adapter.spec.ts +++ b/feature-libs/pickup-in-store/occ/adapters/occ-pickup-location.adapter.spec.ts @@ -88,7 +88,7 @@ describe(`OccPickupLocationAdapter`, () => { expect(mockReq.request.responseType).toEqual('json'); }); it('should call normalized http error for getStoreDetails', fakeAsync(() => { - spyOn(httpClient, 'get').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'get').and.returnValue(throwError(() => mockJaloError)); let result: HttpErrorModel | undefined; const subscription = occAdapter .getStoreDetails(storeName) diff --git a/feature-libs/pickup-in-store/occ/adapters/occ-pickup-location.adapter.ts b/feature-libs/pickup-in-store/occ/adapters/occ-pickup-location.adapter.ts index aa58edac811..ebf0ec98231 100644 --- a/feature-libs/pickup-in-store/occ/adapters/occ-pickup-location.adapter.ts +++ b/feature-libs/pickup-in-store/occ/adapters/occ-pickup-location.adapter.ts @@ -13,7 +13,7 @@ import { PointOfService, } from '@spartacus/core'; import { PickupLocationAdapter } from '@spartacus/pickup-in-store/core'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; @Injectable() @@ -36,9 +36,9 @@ export class OccPickupLocationAdapter implements PickupLocationAdapter { }) ) .pipe( - catchError((error: any) => - throwError(normalizeHttpError(error, this.logger)) - ) + catchError((error: any) => { + throw normalizeHttpError(error, this.logger); + }) ); } } diff --git a/feature-libs/pickup-in-store/occ/adapters/occ-stock.adapter.spec.ts b/feature-libs/pickup-in-store/occ/adapters/occ-stock.adapter.spec.ts index 69eae0f9ee8..b114ebffddc 100644 --- a/feature-libs/pickup-in-store/occ/adapters/occ-stock.adapter.spec.ts +++ b/feature-libs/pickup-in-store/occ/adapters/occ-stock.adapter.spec.ts @@ -85,7 +85,7 @@ describe(`OccStockAdapter`, () => { expect(mockReq.request.responseType).toEqual('json'); }); it('should call normalized http error for loadStockLevels', fakeAsync(() => { - spyOn(httpClient, 'get').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'get').and.returnValue(throwError(() => mockJaloError)); let result: HttpErrorModel | undefined; const subscription = occAdapter .loadStockLevels(productCode, locationParam) @@ -115,7 +115,7 @@ describe(`OccStockAdapter`, () => { expect(mockReq.request.responseType).toEqual('json'); }); it('should call normalized http error for loadStockLevelAtStore', fakeAsync(() => { - spyOn(httpClient, 'get').and.returnValue(throwError(mockJaloError)); + spyOn(httpClient, 'get').and.returnValue(throwError(() => mockJaloError)); let result: HttpErrorModel | undefined; const subscription = occAdapter .loadStockLevelAtStore(productCode, storeName) diff --git a/feature-libs/pickup-in-store/occ/adapters/occ-stock.adapter.ts b/feature-libs/pickup-in-store/occ/adapters/occ-stock.adapter.ts index e7dc6fefa93..47f706fe289 100644 --- a/feature-libs/pickup-in-store/occ/adapters/occ-stock.adapter.ts +++ b/feature-libs/pickup-in-store/occ/adapters/occ-stock.adapter.ts @@ -15,7 +15,7 @@ import { } from '@spartacus/core'; import { StockAdapter } from '@spartacus/pickup-in-store/core'; import { LocationSearchParams } from '@spartacus/pickup-in-store/root'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; /** @@ -44,9 +44,9 @@ export class OccStockAdapter implements StockAdapter { }) ) .pipe( - catchError((error: any) => - throwError(normalizeHttpError(error, this.logger)) - ) + catchError((error: any) => { + throw normalizeHttpError(error, this.logger); + }) ); } @@ -61,9 +61,9 @@ export class OccStockAdapter implements StockAdapter { }) ) .pipe( - catchError((error: any) => - throwError(normalizeHttpError(error, this.logger)) - ) + catchError((error: any) => { + throw normalizeHttpError(error, this.logger); + }) ); } } diff --git a/feature-libs/pickup-in-store/package.json b/feature-libs/pickup-in-store/package.json index 66c448d7a1c..6bab9907d49 100644 --- a/feature-libs/pickup-in-store/package.json +++ b/feature-libs/pickup-in-store/package.json @@ -41,7 +41,7 @@ "@spartacus/styles": "6.6.0-1", "@spartacus/user": "6.6.0-1", "bootstrap": "^4.6.2", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/feature-libs/product-configurator/karma.conf.js b/feature-libs/product-configurator/karma.conf.js index d57a13f41d8..4bd7aea6780 100644 --- a/feature-libs/product-configurator/karma.conf.js +++ b/feature-libs/product-configurator/karma.conf.js @@ -4,9 +4,8 @@ module.exports = function (config) { config.set({ basePath: '', - frameworks: ['parallel', 'jasmine', '@angular-devkit/build-angular'], + frameworks: ['jasmine', '@angular-devkit/build-angular'], plugins: [ - require('karma-parallel'), require('karma-jasmine'), require('karma-coverage'), require('karma-chrome-launcher'), @@ -14,10 +13,6 @@ module.exports = function (config) { require('@angular-devkit/build-angular/plugins/karma'), require('karma-junit-reporter'), ], - parallelOptions: { - executors: 2, - shardStrategy: 'round-robin', - }, client: { clearContext: false, // leave Jasmine Spec Runner output visible in browser jasmine: { diff --git a/feature-libs/product-configurator/package.json b/feature-libs/product-configurator/package.json index b8278f76ebf..d5ac664c16d 100644 --- a/feature-libs/product-configurator/package.json +++ b/feature-libs/product-configurator/package.json @@ -40,7 +40,7 @@ "@spartacus/schematics": "6.6.0-1", "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/feature-libs/product-configurator/rulebased/components/attribute/header/configurator-attribute-header.component.spec.ts b/feature-libs/product-configurator/rulebased/components/attribute/header/configurator-attribute-header.component.spec.ts index 37af7b6dcb2..f58fb929e24 100644 --- a/feature-libs/product-configurator/rulebased/components/attribute/header/configurator-attribute-header.component.spec.ts +++ b/feature-libs/product-configurator/rulebased/components/attribute/header/configurator-attribute-header.component.spec.ts @@ -10,25 +10,24 @@ import { CommonConfigurator, ConfiguratorModelUtils, } from '@spartacus/product-configurator/common'; -import { CommonConfiguratorTestUtilsService } from '../../../../common/testing/common-configurator-test-utils.service'; -import { Configurator } from '../../../core/model/configurator.model'; -import { ConfiguratorAttributeHeaderComponent } from './configurator-attribute-header.component'; -import { ConfiguratorCommonsService } from '../../../core/facade/configurator-commons.service'; -import { ConfiguratorGroupsService } from '../../../core/facade/configurator-groups.service'; -import { ConfiguratorStorefrontUtilsService } from '../../service/configurator-storefront-utils.service'; -import * as ConfigurationTestData from '../../../testing/configurator-test-data'; -import { ConfiguratorUISettingsConfig } from '../../config/configurator-ui-settings.config'; import { + ICON_TYPE, IconLoaderService, IconModule, - ICON_TYPE, } from '@spartacus/storefront'; -import { cold } from 'jasmine-marbles'; +import { getTestScheduler } from 'jasmine-marbles'; import { MockFeatureLevelDirective } from 'projects/storefrontlib/shared/test/mock-feature-level-directive'; import { Observable, of } from 'rxjs'; -import { TestScheduler } from 'rxjs/testing'; +import { CommonConfiguratorTestUtilsService } from '../../../../common/testing/common-configurator-test-utils.service'; +import { ConfiguratorCommonsService } from '../../../core/facade/configurator-commons.service'; +import { ConfiguratorGroupsService } from '../../../core/facade/configurator-groups.service'; +import { Configurator } from '../../../core/model/configurator.model'; +import * as ConfigurationTestData from '../../../testing/configurator-test-data'; import { ConfiguratorTestUtils } from '../../../testing/configurator-test-utils'; +import { ConfiguratorUISettingsConfig } from '../../config/configurator-ui-settings.config'; +import { ConfiguratorStorefrontUtilsService } from '../../service/configurator-storefront-utils.service'; import { ConfiguratorAttributeCompositionContext } from '../composition/configurator-attribute-composition.model'; +import { ConfiguratorAttributeHeaderComponent } from './configurator-attribute-header.component'; export class MockIconFontLoaderService { useSvg(_iconType: ICON_TYPE) { @@ -969,12 +968,9 @@ describe('ConfigAttributeHeaderComponent', () => { describe('Focus selected value', () => { it('should call focusValue with attribute', () => { - const testScheduler = new TestScheduler((actual, expected) => { - expect(actual).toEqual(expected); - }); //we need to run the test in a test scheduler //because of the delay() in method focusAttribute - testScheduler.run(() => { + getTestScheduler().run(({ cold, flush }) => { component.groupType = Configurator.GroupType.CONFLICT_GROUP; component.attribute.groupId = ConfigurationTestData.GROUP_ID_2; const configurationLoading = cold('-a-b', { @@ -990,22 +986,21 @@ describe('ConfigAttributeHeaderComponent', () => { fixture.detectChanges(); component['focusValue'](component.attribute); - }); - expect( - configuratorStorefrontUtilsService.focusValue - ).toHaveBeenCalledTimes(1); + flush(); + + expect( + configuratorStorefrontUtilsService.focusValue + ).toHaveBeenCalledTimes(1); + }); }); }); describe('Scroll to configuration element', () => { it('should call scrollToConfigurationElement', () => { - const testScheduler = new TestScheduler((actual, expected) => { - expect(actual).toEqual(expected); - }); //we need to run the test in a test scheduler //because of the delay() in method focusAttribute - testScheduler.run(() => { + getTestScheduler().run(({ cold, flush }) => { component.groupType = Configurator.GroupType.CONFLICT_GROUP; component.attribute.groupId = ConfigurationTestData.GROUP_ID_2; @@ -1026,11 +1021,13 @@ describe('ConfigAttributeHeaderComponent', () => { fixture.detectChanges(); component['scrollToAttribute'](ConfigurationTestData.GROUP_ID_2); - }); - expect( - configuratorStorefrontUtilsService.scrollToConfigurationElement - ).toHaveBeenCalledTimes(1); + flush(); + + expect( + configuratorStorefrontUtilsService.scrollToConfigurationElement + ).toHaveBeenCalledTimes(1); + }); }); }); diff --git a/feature-libs/product-configurator/rulebased/components/previous-next-buttons/configurator-previous-next-buttons.component.spec.ts b/feature-libs/product-configurator/rulebased/components/previous-next-buttons/configurator-previous-next-buttons.component.spec.ts index a24f373eef7..e9b5f41b6d6 100644 --- a/feature-libs/product-configurator/rulebased/components/previous-next-buttons/configurator-previous-next-buttons.component.spec.ts +++ b/feature-libs/product-configurator/rulebased/components/previous-next-buttons/configurator-previous-next-buttons.component.spec.ts @@ -11,9 +11,8 @@ import { CommonConfiguratorUtilsService, ConfiguratorModelUtils, } from '@spartacus/product-configurator/common'; -import { cold } from 'jasmine-marbles'; +import { cold, getTestScheduler } from 'jasmine-marbles'; import { Observable, of } from 'rxjs'; -import { TestScheduler } from 'rxjs/testing'; import { CommonConfiguratorTestUtilsService } from '../../../common/testing/common-configurator-test-utils.service'; import { ConfiguratorCommonsService } from '../../core/facade/configurator-commons.service'; import { ConfiguratorGroupsService } from '../../core/facade/configurator-groups.service'; @@ -311,12 +310,9 @@ describe('ConfigPreviousNextButtonsComponent', () => { }); it('should call focusFirstAttribute', () => { - const testScheduler = new TestScheduler((actual, expected) => { - expect(actual).toEqual(expected); - }); //we need to run the test in a test scheduler //because of the delay() in method focusFirstAttribute - testScheduler.run(() => { + getTestScheduler().run(({ cold, flush }) => { const configurationLoading = cold('-a-b', { a: true, b: false, @@ -326,10 +322,11 @@ describe('ConfigPreviousNextButtonsComponent', () => { 'isConfigurationLoading' ).and.returnValue(configurationLoading); classUnderTest['focusFirstAttribute'](); + flush(); + expect( + configuratorStorefrontUtilsService.focusFirstAttribute + ).toHaveBeenCalledTimes(1); }); - expect( - configuratorStorefrontUtilsService.focusFirstAttribute - ).toHaveBeenCalledTimes(1); }); describe('Accessibility', () => { diff --git a/feature-libs/product-configurator/rulebased/core/facade/configurator-cart.service.spec.ts b/feature-libs/product-configurator/rulebased/core/facade/configurator-cart.service.spec.ts index 2fa17f13aca..85840b4ffe0 100644 --- a/feature-libs/product-configurator/rulebased/core/facade/configurator-cart.service.spec.ts +++ b/feature-libs/product-configurator/rulebased/core/facade/configurator-cart.service.spec.ts @@ -1,4 +1,3 @@ -import { Type } from '@angular/core'; import { TestBed, waitForAsync } from '@angular/core/testing'; import * as ngrxStore from '@ngrx/store'; import { Store, StoreModule } from '@ngrx/store'; @@ -131,13 +130,9 @@ describe('ConfiguratorCartService', () => { }) ); beforeEach(() => { - serviceUnderTest = TestBed.inject( - ConfiguratorCartService as Type - ); - store = TestBed.inject(Store as Type>); - configuratorUtils = TestBed.inject( - CommonConfiguratorUtilsService as Type - ); + serviceUnderTest = TestBed.inject(ConfiguratorCartService); + store = TestBed.inject(Store); + configuratorUtils = TestBed.inject(CommonConfiguratorUtilsService); OWNER_CART_ENTRY = { id: CART_ENTRY_ID, type: CommonConfigurator.OwnerType.CART_ENTRY, @@ -183,6 +178,12 @@ describe('ConfiguratorCartService', () => { }); it('should dispatch ReadCartEntryConfiguration action in case configuration is not present so far', () => { + checkoutLoadingObs = of({ + loading: false, + error: false, + data: undefined, + }); + const params: CommonConfigurator.ReadConfigurationFromCartEntryParameters = { owner: OWNER_CART_ENTRY, diff --git a/feature-libs/product-configurator/rulebased/core/state/effects/configurator-basic.effect.spec.ts b/feature-libs/product-configurator/rulebased/core/state/effects/configurator-basic.effect.spec.ts index b3ff3c5203f..35ff841c7db 100644 --- a/feature-libs/product-configurator/rulebased/core/state/effects/configurator-basic.effect.spec.ts +++ b/feature-libs/product-configurator/rulebased/core/state/effects/configurator-basic.effect.spec.ts @@ -13,11 +13,11 @@ import { import { cold, hot } from 'jasmine-marbles'; import { Observable, of, throwError } from 'rxjs'; import { + ATTRIBUTE_1_CHECKBOX, CONFIG_ID, - GROUP_ID_CONFLICT_HEADER, GROUP_ID_CONFLICT_1, GROUP_ID_CONFLICT_2, - ATTRIBUTE_1_CHECKBOX, + GROUP_ID_CONFLICT_HEADER, } from '../../../testing/configurator-test-data'; import { ConfiguratorTestUtils } from '../../../testing/configurator-test-utils'; import { RulebasedConfiguratorConnector } from '../../connectors/rulebased-configurator.connector'; @@ -305,7 +305,7 @@ describe('ConfiguratorEffect', () => { }); it('should emit a fail action in case something goes wrong', () => { - createMock.and.returnValue(throwError(errorResponse)); + createMock.and.returnValue(throwError(() => errorResponse)); const action = new ConfiguratorActions.CreateConfiguration({ owner: productConfiguration.owner, @@ -344,7 +344,7 @@ describe('ConfiguratorEffect', () => { }); it('should emit a fail action in case connector raises an error', () => { - readMock.and.returnValue(throwError(errorResponse)); + readMock.and.returnValue(throwError(() => errorResponse)); const action = new ConfiguratorActions.ReadConfiguration({ configuration: productConfiguration, groupId: '', @@ -398,7 +398,7 @@ describe('ConfiguratorEffect', () => { }); it('should emit a fail action in case something goes wrong', () => { - overviewMock.and.returnValue(throwError(errorResponse)); + overviewMock.and.returnValue(throwError(() => errorResponse)); const overviewAction = new ConfiguratorActions.GetConfigurationOverview( productConfiguration ); @@ -438,7 +438,7 @@ describe('ConfiguratorEffect', () => { }); it('should emit a fail action in case something goes wrong', () => { - updateOverviewMock.and.returnValue(throwError(errorResponse)); + updateOverviewMock.and.returnValue(throwError(() => errorResponse)); const overviewAction = new ConfiguratorActions.UpdateConfigurationOverview( productConfiguration @@ -480,7 +480,7 @@ describe('ConfiguratorEffect', () => { }); it('should emit a fail action in case something goes wrong', () => { - updateConfigurationMock.and.returnValue(throwError(errorResponse)); + updateConfigurationMock.and.returnValue(throwError(() => errorResponse)); const payloadInput = productConfiguration; const action = new ConfiguratorActions.UpdateConfiguration(payloadInput); @@ -510,7 +510,7 @@ describe('ConfiguratorEffect', () => { }); it('should emit a fail action in case something goes wrong', () => { - readPriceSummaryMock.and.returnValue(throwError(errorResponse)); + readPriceSummaryMock.and.returnValue(throwError(() => errorResponse)); const payloadInput = productConfiguration; const updatePriceSummaryAction = new ConfiguratorActions.UpdatePriceSummary(payloadInput); @@ -798,7 +798,7 @@ describe('ConfiguratorEffect', () => { }); it('should emit ReadConfigurationFail in case read call is not successful', () => { - readMock.and.returnValue(throwError(errorResponse)); + readMock.and.returnValue(throwError(() => errorResponse)); const payloadInput: Configurator.Configuration = { ...ConfiguratorTestUtils.createConfiguration(configId, owner), productCode: productCode, diff --git a/feature-libs/product-configurator/rulebased/core/state/effects/configurator-cart.effect.spec.ts b/feature-libs/product-configurator/rulebased/core/state/effects/configurator-cart.effect.spec.ts index f8fca39b01a..0dcd9e015ee 100644 --- a/feature-libs/product-configurator/rulebased/core/state/effects/configurator-cart.effect.spec.ts +++ b/feature-libs/product-configurator/rulebased/core/state/effects/configurator-cart.effect.spec.ts @@ -440,7 +440,7 @@ describe('ConfiguratorCartEffect', () => { }); it('should emit a fail action if something goes wrong', () => { - readFromCartEntryObs = throwError(errorResponse); + readFromCartEntryObs = throwError(() => errorResponse); const completion = new ConfiguratorActions.ReadCartEntryConfigurationFail( { @@ -484,7 +484,7 @@ describe('ConfiguratorCartEffect', () => { it('should emit a fail action if something goes wrong', () => { readConfigurationForOrderEntryMock.and.returnValue( - throwError(errorResponse) + throwError(() => errorResponse) ); const readFromOrderEntry: CommonConfigurator.ReadConfigurationFromOrderEntryParameters = { @@ -573,7 +573,7 @@ describe('ConfiguratorCartEffect', () => { }); it('should emit CartAddEntryFail in case add to cart call is not successful', () => { - addToCartMock.and.returnValue(throwError(errorResponse)); + addToCartMock.and.returnValue(throwError(() => errorResponse)); const payloadInput: Configurator.AddToCartParameters = { userId: userId, cartId: cartId, @@ -620,7 +620,7 @@ describe('ConfiguratorCartEffect', () => { }); it('should emit AddToCartFail in case update cart entry call is not successful', () => { - updateCartEntryMock.and.returnValue(throwError(errorResponse)); + updateCartEntryMock.and.returnValue(throwError(() => errorResponse)); const action = new ConfiguratorActions.UpdateCartEntry( payloadInputUpdateConfiguration diff --git a/feature-libs/product-configurator/rulebased/core/state/effects/configurator-variant.effect.spec.ts b/feature-libs/product-configurator/rulebased/core/state/effects/configurator-variant.effect.spec.ts index b3808e58df3..e34e288291c 100644 --- a/feature-libs/product-configurator/rulebased/core/state/effects/configurator-variant.effect.spec.ts +++ b/feature-libs/product-configurator/rulebased/core/state/effects/configurator-variant.effect.spec.ts @@ -156,7 +156,7 @@ describe('ConfiguratorVariantEffect', () => { }); it('should emit a fail action in case something goes wrong', () => { - searchVariantsMock.and.returnValue(throwError(errorResponse)); + searchVariantsMock.and.returnValue(throwError(() => errorResponse)); const action = new ConfiguratorActions.SearchVariants(productConfiguration); diff --git a/feature-libs/product-configurator/rulebased/root/cpq/interceptor/cpq-access-storage.service.ts b/feature-libs/product-configurator/rulebased/root/cpq/interceptor/cpq-access-storage.service.ts index 6b93d2c137e..2d9e56dac93 100644 --- a/feature-libs/product-configurator/rulebased/root/cpq/interceptor/cpq-access-storage.service.ts +++ b/feature-libs/product-configurator/rulebased/root/cpq/interceptor/cpq-access-storage.service.ts @@ -35,15 +35,18 @@ export class CpqAccessStorageService implements OnDestroy { ngOnDestroy(): void { this.currentCpqAccessSubscription?.unsubscribe(); this.currentAuthServiceSubscription?.unsubscribe(); + this._cpqAccessDataErrorSubscription?.unsubscribe(); } protected cpqAccessData$: Observable; protected currentCpqAccessSubscription: Subscription; protected currentAuthServiceSubscription: Subscription; protected _cpqAccessData$: BehaviorSubject; + protected _cpqAccessDataError = false; + protected _cpqAccessDataErrorSubscription: Subscription | undefined; getCpqAccessData(): Observable { - if (!this.cpqAccessData$ || this._cpqAccessData$.hasError) { + if (!this.cpqAccessData$ || this._cpqAccessDataError) { this.initCpqAccessData(); } return this.cpqAccessData$; @@ -73,6 +76,11 @@ export class CpqAccessStorageService implements OnDestroy { protected initCpqAccessData() { this._cpqAccessData$ = new BehaviorSubject(this.EXPIRED_TOKEN); + this._cpqAccessDataError = false; + this._cpqAccessDataErrorSubscription?.unsubscribe(); + this._cpqAccessDataErrorSubscription = this._cpqAccessData$.subscribe({ + error: () => (this._cpqAccessDataError = true), + }); this.cpqAccessData$ = this._cpqAccessData$.pipe( // Never expose expired tokens - either cache was invalidated with expired token, // or the cached one expired before a new one was fetched. diff --git a/feature-libs/product-configurator/rulebased/root/cpq/interceptor/cpq-configurator-rest.interceptor.spec.ts b/feature-libs/product-configurator/rulebased/root/cpq/interceptor/cpq-configurator-rest.interceptor.spec.ts index 5b08c5346ec..65561b0b18e 100644 --- a/feature-libs/product-configurator/rulebased/root/cpq/interceptor/cpq-configurator-rest.interceptor.spec.ts +++ b/feature-libs/product-configurator/rulebased/root/cpq/interceptor/cpq-configurator-rest.interceptor.spec.ts @@ -92,7 +92,7 @@ describe('CpqConfiguratorRestInterceptor', () => { capturedRequestsStack.push(request); const cpqResponse = cpqResponseStack.pop(); return cpqResponse instanceof HttpErrorResponse - ? throwError(cpqResponse) + ? throwError(() => cpqResponse) : of(cpqResponse); } ); diff --git a/feature-libs/product-configurator/rulebased/root/cpq/interceptor/cpq-configurator-rest.interceptor.ts b/feature-libs/product-configurator/rulebased/root/cpq/interceptor/cpq-configurator-rest.interceptor.ts index f03cab5bfb9..fa24e906d71 100644 --- a/feature-libs/product-configurator/rulebased/root/cpq/interceptor/cpq-configurator-rest.interceptor.ts +++ b/feature-libs/product-configurator/rulebased/root/cpq/interceptor/cpq-configurator-rest.interceptor.ts @@ -81,7 +81,7 @@ export class CpqConfiguratorRestInterceptor implements HttpInterceptor { ); } } - return throwError(errorResponse); //propagate error + return throwError(() => errorResponse); //propagate error } protected extractCpqSessionId(response: HttpEvent) { diff --git a/feature-libs/product-configurator/textfield/core/state/effects/configurator-textfield.effect.spec.ts b/feature-libs/product-configurator/textfield/core/state/effects/configurator-textfield.effect.spec.ts index 49dbcc3dd22..370de95be14 100644 --- a/feature-libs/product-configurator/textfield/core/state/effects/configurator-textfield.effect.spec.ts +++ b/feature-libs/product-configurator/textfield/core/state/effects/configurator-textfield.effect.spec.ts @@ -123,7 +123,7 @@ describe('ConfiguratorTextfieldEffect', () => { }); it('should emit a fail action in case something goes wrong', () => { - createMock.and.returnValue(throwError(errorResponse)); + createMock.and.returnValue(throwError(() => errorResponse)); const payloadInput = { productCode: productCode, owner: ConfiguratorModelUtils.createInitialOwner(), @@ -164,7 +164,7 @@ describe('ConfiguratorTextfieldEffect', () => { }); it('should emit a fail action in case read from cart leads to an error', () => { - readFromCartEntryMock.and.returnValue(throwError(errorResponse)); + readFromCartEntryMock.and.returnValue(throwError(() => errorResponse)); const payloadInput: CommonConfigurator.ReadConfigurationFromCartEntryParameters = { owner: ConfiguratorModelUtils.createInitialOwner(), @@ -207,7 +207,7 @@ describe('ConfiguratorTextfieldEffect', () => { }); it('should emit a fail action in case read from order entry leads to an error', () => { - readFromOrderEntryMock.and.returnValue(throwError(errorResponse)); + readFromOrderEntryMock.and.returnValue(throwError(() => errorResponse)); const payloadInput: CommonConfigurator.ReadConfigurationFromOrderEntryParameters = { owner: ConfiguratorModelUtils.createInitialOwner(), @@ -266,7 +266,7 @@ describe('ConfiguratorTextfieldEffect', () => { }); it('should emit AddToCartFail in case add to cart call is not successful', () => { - addToCartMock.and.returnValue(throwError(errorResponse)); + addToCartMock.and.returnValue(throwError(() => errorResponse)); const payloadInput = { userId: userId, cartId: cartId, @@ -317,7 +317,7 @@ describe('ConfiguratorTextfieldEffect', () => { }); it('should emit CartUpdateEntryFail in case update cart entry is not successful', () => { - updateCartEntryMock.and.returnValue(throwError(errorResponse)); + updateCartEntryMock.and.returnValue(throwError(() => errorResponse)); const payloadInput: ConfiguratorTextfield.UpdateCartEntryParameters = { userId: userId, cartId: cartId, diff --git a/feature-libs/product/future-stock/occ/adapters/occ-future-stock.adapter.ts b/feature-libs/product/future-stock/occ/adapters/occ-future-stock.adapter.ts index 443893c027e..190868de0ae 100644 --- a/feature-libs/product/future-stock/occ/adapters/occ-future-stock.adapter.ts +++ b/feature-libs/product/future-stock/occ/adapters/occ-future-stock.adapter.ts @@ -21,7 +21,7 @@ import { ProductFutureStock, ProductFutureStockList, } from '@spartacus/product/future-stock/core'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; @Injectable() @@ -41,9 +41,9 @@ export class OccFutureStockAdapter implements FutureStockAdapter { return this.http .get(this.getFutureStockEndpoint(userId, productCode)) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), this.converter.pipeable(FUTURE_STOCK_NORMALIZER) ); } @@ -57,9 +57,9 @@ export class OccFutureStockAdapter implements FutureStockAdapter { this.getFutureStocksEndpoint(userId, productCodes) ) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), this.converter.pipeable(FUTURE_STOCK_LIST_NORMALIZER) ); } diff --git a/feature-libs/product/package.json b/feature-libs/product/package.json index 0693d2d2556..917726b684f 100644 --- a/feature-libs/product/package.json +++ b/feature-libs/product/package.json @@ -34,7 +34,7 @@ "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", "bootstrap": "^4.6.2", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/feature-libs/qualtrics/package.json b/feature-libs/qualtrics/package.json index 4829d4a94cf..4abdfd2fc03 100644 --- a/feature-libs/qualtrics/package.json +++ b/feature-libs/qualtrics/package.json @@ -34,7 +34,7 @@ "@spartacus/schematics": "6.6.0-1", "@spartacus/styles": "6.6.0-1", "bootstrap": "^4.6.2", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/feature-libs/requested-delivery-date/karma.conf.js b/feature-libs/requested-delivery-date/karma.conf.js index a143424cf73..8d063cb2395 100644 --- a/feature-libs/requested-delivery-date/karma.conf.js +++ b/feature-libs/requested-delivery-date/karma.conf.js @@ -4,9 +4,8 @@ module.exports = function (config) { config.set({ basePath: '', - frameworks: ['parallel', 'jasmine', '@angular-devkit/build-angular'], + frameworks: ['jasmine', '@angular-devkit/build-angular'], plugins: [ - require('karma-parallel'), require('karma-jasmine'), require('karma-coverage'), require('karma-chrome-launcher'), diff --git a/feature-libs/requested-delivery-date/package.json b/feature-libs/requested-delivery-date/package.json index e74a4f8c214..31d35677748 100644 --- a/feature-libs/requested-delivery-date/package.json +++ b/feature-libs/requested-delivery-date/package.json @@ -36,7 +36,7 @@ "@spartacus/schematics": "6.6.0-1", "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/feature-libs/smartedit/package.json b/feature-libs/smartedit/package.json index b8d39562e26..2f7e30b83ef 100644 --- a/feature-libs/smartedit/package.json +++ b/feature-libs/smartedit/package.json @@ -25,7 +25,7 @@ "@angular/core": "^15.2.9", "@spartacus/core": "6.6.0-1", "@spartacus/schematics": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/feature-libs/storefinder/package.json b/feature-libs/storefinder/package.json index 8b91468f1a5..0510f685077 100644 --- a/feature-libs/storefinder/package.json +++ b/feature-libs/storefinder/package.json @@ -37,7 +37,7 @@ "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", "bootstrap": "^4.6.2", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/feature-libs/tracking/package.json b/feature-libs/tracking/package.json index f98d35f2b20..f3838f1058a 100644 --- a/feature-libs/tracking/package.json +++ b/feature-libs/tracking/package.json @@ -29,7 +29,7 @@ "@angular/core": "^15.2.9", "@spartacus/core": "6.6.0-1", "@spartacus/schematics": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/feature-libs/user/account/occ/adapters/occ-user-account.adapter.ts b/feature-libs/user/account/occ/adapters/occ-user-account.adapter.ts index 0c875ee3297..c772ce98836 100644 --- a/feature-libs/user/account/occ/adapters/occ-user-account.adapter.ts +++ b/feature-libs/user/account/occ/adapters/occ-user-account.adapter.ts @@ -18,7 +18,7 @@ import { UserAccountAdapter, } from '@spartacus/user/account/core'; import { User } from '@spartacus/user/account/root'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; @Injectable() @@ -34,7 +34,9 @@ export class OccUserAccountAdapter implements UserAccountAdapter { load(userId: string): Observable { const url = this.occEndpoints.buildUrl('user', { urlParams: { userId } }); return this.http.get(url).pipe( - catchError((error) => throwError(normalizeHttpError(error, this.logger))), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), this.converter.pipeable(USER_ACCOUNT_NORMALIZER) ); } diff --git a/feature-libs/user/package.json b/feature-libs/user/package.json index 3aa86177076..5e4dffdbc98 100644 --- a/feature-libs/user/package.json +++ b/feature-libs/user/package.json @@ -36,7 +36,7 @@ "@spartacus/schematics": "6.6.0-1", "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/feature-libs/user/profile/components/close-account/components/close-account-modal/close-account-modal.component.spec.ts b/feature-libs/user/profile/components/close-account/components/close-account-modal/close-account-modal.component.spec.ts index a25c7a2859a..3710faa9354 100644 --- a/feature-libs/user/profile/components/close-account/components/close-account-modal/close-account-modal.component.spec.ts +++ b/feature-libs/user/profile/components/close-account/components/close-account-modal/close-account-modal.component.spec.ts @@ -125,7 +125,7 @@ describe('CloseAccountModalComponent', () => { it('should dismiss modal when account failed to close', () => { spyOn(component, 'onError').and.callThrough(); // spyOn(launchDialogService, 'closeDialog').and.callThrough(); - (userFacade.close as any).and.returnValue(throwError(undefined)); + (userFacade.close as any).and.returnValue(throwError(() => undefined)); component.ngOnInit(); component.closeAccount(); diff --git a/feature-libs/user/profile/components/reset-password/reset-password-component.service.spec.ts b/feature-libs/user/profile/components/reset-password/reset-password-component.service.spec.ts index c5c0d22951c..b3bd0502062 100644 --- a/feature-libs/user/profile/components/reset-password/reset-password-component.service.spec.ts +++ b/feature-libs/user/profile/components/reset-password/reset-password-component.service.spec.ts @@ -174,7 +174,7 @@ describe('ResetPasswordComponentService', () => { const error = new HttpErrorModel(); error.details = [{ message: 'error message' }]; spyOn(userPasswordService, 'reset').and.returnValue( - throwError(error) + throwError(() => error) ); service.resetPassword(resetToken); expect(globalMessageService.add).toHaveBeenCalledWith( @@ -184,13 +184,17 @@ describe('ResetPasswordComponentService', () => { }); it('should not show error message', () => { - spyOn(userPasswordService, 'reset').and.returnValue(throwError(null)); + spyOn(userPasswordService, 'reset').and.returnValue( + throwError(() => null) + ); service.resetPassword(resetToken); expect(globalMessageService.add).not.toHaveBeenCalled(); }); it('should not show error message', () => { - spyOn(userPasswordService, 'reset').and.returnValue(throwError({})); + spyOn(userPasswordService, 'reset').and.returnValue( + throwError(() => ({})) + ); service.resetPassword(resetToken); expect(globalMessageService.add).not.toHaveBeenCalled(); }); @@ -198,7 +202,9 @@ describe('ResetPasswordComponentService', () => { }); it('should not reset invalid form', () => { - spyOn(userPasswordService, 'reset').and.returnValue(throwError({})); + spyOn(userPasswordService, 'reset').and.returnValue( + throwError(() => ({})) + ); passwordConfirm.setValue('Diff123!'); service.resetPassword(resetToken); expect(userPasswordService.reset).not.toHaveBeenCalled(); diff --git a/feature-libs/user/profile/occ/adapters/occ-user-profile.adapter.ts b/feature-libs/user/profile/occ/adapters/occ-user-profile.adapter.ts index 78b52d22da8..85dbe1db8a8 100644 --- a/feature-libs/user/profile/occ/adapters/occ-user-profile.adapter.ts +++ b/feature-libs/user/profile/occ/adapters/occ-user-profile.adapter.ts @@ -24,7 +24,7 @@ import { UserProfileAdapter, } from '@spartacus/user/profile/core'; import { Title, UserSignUp } from '@spartacus/user/profile/root'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; const CONTENT_TYPE_JSON_HEADER = { 'Content-Type': 'application/json' }; @@ -48,13 +48,11 @@ export class OccUserProfileAdapter implements UserProfileAdapter { : 'user'; const url = this.occEndpoints.buildUrl(endpoint, { urlParams: { userId } }); user = this.converter.convert(user, USER_PROFILE_SERIALIZER); - return this.http - .patch(url, user) - .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ) - ); + return this.http.patch(url, user).pipe( + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }) + ); } register(user: UserSignUp): Observable { @@ -66,7 +64,9 @@ export class OccUserProfileAdapter implements UserProfileAdapter { user = this.converter.convert(user, USER_SIGN_UP_SERIALIZER); return this.http.post(url, user, { headers }).pipe( - catchError((error) => throwError(normalizeHttpError(error, this.logger))), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), this.converter.pipeable(USER_PROFILE_NORMALIZER) ); } @@ -83,7 +83,9 @@ export class OccUserProfileAdapter implements UserProfileAdapter { .set('password', password); return this.http.post(url, httpParams, { headers }).pipe( - catchError((error) => throwError(normalizeHttpError(error, this.logger))), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), this.converter.pipeable(USER_PROFILE_NORMALIZER) ); } @@ -98,13 +100,11 @@ export class OccUserProfileAdapter implements UserProfileAdapter { ...CONTENT_TYPE_URLENCODED_HEADER, }); headers = InterceptorUtil.createHeader(USE_CLIENT_TOKEN, true, headers); - return this.http - .post(url, httpParams, { headers }) - .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ) - ); + return this.http.post(url, httpParams, { headers }).pipe( + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }) + ); } resetPassword(token: string, newPassword: string): Observable { @@ -114,13 +114,11 @@ export class OccUserProfileAdapter implements UserProfileAdapter { }); headers = InterceptorUtil.createHeader(USE_CLIENT_TOKEN, true, headers); - return this.http - .post(url, { token, newPassword }, { headers }) - .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ) - ); + return this.http.post(url, { token, newPassword }, { headers }).pipe( + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }) + ); } updateEmail( @@ -137,13 +135,11 @@ export class OccUserProfileAdapter implements UserProfileAdapter { const headers = new HttpHeaders({ ...CONTENT_TYPE_URLENCODED_HEADER, }); - return this.http - .put(url, httpParams, { headers }) - .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ) - ); + return this.http.put(url, httpParams, { headers }).pipe( + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }) + ); } updatePassword( @@ -160,13 +156,11 @@ export class OccUserProfileAdapter implements UserProfileAdapter { const headers = new HttpHeaders({ ...CONTENT_TYPE_URLENCODED_HEADER, }); - return this.http - .put(url, httpParams, { headers }) - .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ) - ); + return this.http.put(url, httpParams, { headers }).pipe( + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }) + ); } close(userId: string): Observable { @@ -174,19 +168,19 @@ export class OccUserProfileAdapter implements UserProfileAdapter { ? 'userCloseAccount' : 'user'; const url = this.occEndpoints.buildUrl(endpoint, { urlParams: { userId } }); - return this.http - .delete(url) - .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ) - ); + return this.http.delete(url).pipe( + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }) + ); } loadTitles(): Observable { const url = this.occEndpoints.buildUrl('titles'); return this.http.get(url).pipe( - catchError((error) => throwError(normalizeHttpError(error, this.logger))), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), map((titleList) => titleList.titles ?? []), this.converter.pipeableMany(TITLE_NORMALIZER) ); diff --git a/integration-libs/cdc/core/auth/services/user-authentication/cdc-user-authentication-token.service.ts b/integration-libs/cdc/core/auth/services/user-authentication/cdc-user-authentication-token.service.ts index 99deaaa5435..b9b7a7cb41a 100644 --- a/integration-libs/cdc/core/auth/services/user-authentication/cdc-user-authentication-token.service.ts +++ b/integration-libs/cdc/core/auth/services/user-authentication/cdc-user-authentication-token.service.ts @@ -6,8 +6,12 @@ import { HttpClient, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { AuthConfigService, AuthToken } from '@spartacus/core'; -import { Observable, throwError } from 'rxjs'; +import { + AuthConfigService, + AuthToken, + normalizeHttpError, +} from '@spartacus/core'; +import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; @Injectable() @@ -46,6 +50,10 @@ export class CdcUserAuthenticationTokenService { return this.http .post & { expires_in?: number }>(url, params) - .pipe(catchError((error: any) => throwError(error))); + .pipe( + catchError((error: any) => { + throw normalizeHttpError(error); + }) + ); } } diff --git a/integration-libs/cdc/core/store/effects/cdc-user-addresses.effect.spec.ts b/integration-libs/cdc/core/store/effects/cdc-user-addresses.effect.spec.ts index 25cafb47589..46fa5cca922 100644 --- a/integration-libs/cdc/core/store/effects/cdc-user-addresses.effect.spec.ts +++ b/integration-libs/cdc/core/store/effects/cdc-user-addresses.effect.spec.ts @@ -130,7 +130,7 @@ describe('CDC User Addresses effect', () => { }; spyOn(cdcJSService, 'updateAddressWithoutScreenSet').and.returnValue( - throwError(error) + throwError(() => error) ); const expected = cold('-#', null, error); @@ -178,7 +178,7 @@ describe('CDC User Addresses effect', () => { const expected = cold('-#', null, error); spyOn(cdcJSService, 'updateAddressWithoutScreenSet').and.returnValue( - throwError(error) + throwError(() => error) ); expect(cdcUserAddressesEffect.cdcUpdateUserAddress$).toBeObservable( @@ -224,7 +224,7 @@ describe('CDC User Addresses effect', () => { const expected = cold('-#', null, error); spyOn(cdcJSService, 'updateAddressWithoutScreenSet').and.returnValue( - throwError(error) + throwError(() => error) ); expect(cdcUserAddressesEffect.cdcUpdateUserAddress$).toBeObservable( @@ -274,7 +274,7 @@ describe('CDC User Addresses effect', () => { const expected = cold('-#', null, error); spyOn(cdcJSService, 'updateAddressWithoutScreenSet').and.returnValue( - throwError(error) + throwError(() => error) ); expect(cdcUserAddressesEffect.cdcDeleteUserAddress$).toBeObservable( diff --git a/integration-libs/cdc/package.json b/integration-libs/cdc/package.json index 3d13a229d21..084fd6137ed 100644 --- a/integration-libs/cdc/package.json +++ b/integration-libs/cdc/package.json @@ -36,7 +36,7 @@ "@spartacus/schematics": "6.6.0-1", "@spartacus/storefront": "6.6.0-1", "@spartacus/user": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/integration-libs/cdc/root/cdc-root.module.ts b/integration-libs/cdc/root/cdc-root.module.ts index c3716cb1fce..f2e60a98bde 100644 --- a/integration-libs/cdc/root/cdc-root.module.ts +++ b/integration-libs/cdc/root/cdc-root.module.ts @@ -13,6 +13,7 @@ import { provideDefaultConfigFactory, } from '@spartacus/core'; import { LogoutGuard } from '@spartacus/storefront'; +import { lastValueFrom } from 'rxjs'; import { tap } from 'rxjs/operators'; import { CdcConsentManagementModule } from './consent-management/cdc-consent.module'; import { defaultCdcRoutingConfig } from './config/default-cdc-routing-config'; @@ -24,16 +25,14 @@ export function cdcJsFactory( cdcJsService: CdcJsService, configInit: ConfigInitializerService ): () => Promise { - const func = () => - configInit - .getStable('context', 'cdc') - .pipe( + return () => + lastValueFrom( + configInit.getStable('context', 'cdc').pipe( tap(() => { cdcJsService.initialize(); }) ) - .toPromise(); - return func; + ); } export function defaultCdcComponentsConfig(): CmsConfig { diff --git a/integration-libs/cdc/root/service/cdc-js.service.ts b/integration-libs/cdc/root/service/cdc-js.service.ts index abfb2de8e7e..f9369120208 100644 --- a/integration-libs/cdc/root/service/cdc-js.service.ts +++ b/integration-libs/cdc/root/service/cdc-js.service.ts @@ -201,7 +201,7 @@ export class CdcJsService implements OnDestroy { user: UserSignUp ): Observable<{ status: string }> { if (!user.uid || !user.password) { - return throwError(null); + return throwError(() => null); } else { return this.invokeAPI('accounts.initRegistration', {}).pipe( switchMap((response) => this.onInitRegistrationHandler(user, response)) @@ -220,7 +220,7 @@ export class CdcJsService implements OnDestroy { response: any ): Observable<{ status: string }> { if (!response?.regToken || !user?.uid || !user?.password) { - return throwError(null); + return throwError(() => null); } else { const regSource: string = this.winRef.nativeWindow?.location?.href || ''; return this.invokeAPI('accounts.register', { @@ -459,7 +459,7 @@ export class CdcJsService implements OnDestroy { */ resetPasswordWithoutScreenSet(email: string): Observable<{ status: string }> { if (!email || email?.length === 0) { - return throwError('No email provided'); + return throwError(() => 'No email provided'); } else { return this.invokeAPI('accounts.resetPassword', { loginID: email, @@ -506,7 +506,7 @@ export class CdcJsService implements OnDestroy { !user?.lastName || user?.lastName?.length === 0 ) { - return throwError('User details not provided'); + return throwError(() => 'User details not provided'); } else { const profileObj = { profile: { @@ -543,7 +543,7 @@ export class CdcJsService implements OnDestroy { !newPassword || newPassword?.length === 0 ) { - return throwError('No passwords provided'); + return throwError(() => 'No passwords provided'); } else { return this.invokeAPI(setAccountInfoAPI, { password: oldPassword, @@ -595,14 +595,14 @@ export class CdcJsService implements OnDestroy { !newEmail || newEmail?.length === 0 ) { - return throwError('Email or password not provided'); + return throwError(() => 'Email or password not provided'); } else { //Verify the password by attempting to login return this.getLoggedInUserEmail().pipe( switchMap((user) => { const email = user?.uid; if (!email || email?.length === 0) { - return throwError('Email or password not provided'); + return throwError(() => 'Email or password not provided'); } // Verify the password by attempting to login // - CDC doesn't require to verify password before changing an email, but the default Spartacus requires it. @@ -663,7 +663,7 @@ export class CdcJsService implements OnDestroy { country?: string ): Observable<{ status: string }> { if (!formattedAddress || formattedAddress?.length === 0) { - return throwError('No address provided'); + return throwError(() => 'No address provided'); } else { const profileObj = { address: formattedAddress, diff --git a/integration-libs/cdc/user-account/login-form/cdc-login-form-component.service.spec.ts b/integration-libs/cdc/user-account/login-form/cdc-login-form-component.service.spec.ts index 898242caea6..fd3b95b1a47 100644 --- a/integration-libs/cdc/user-account/login-form/cdc-login-form-component.service.spec.ts +++ b/integration-libs/cdc/user-account/login-form/cdc-login-form-component.service.spec.ts @@ -108,7 +108,7 @@ describe('CdcLoginComponentService', () => { it('should handle a failed request through CDC SDK', () => { cdcJsService.didLoad = createSpy().and.returnValue(of(true)); (cdcJsService.loginUserWithoutScreenSet as jasmine.Spy).and.returnValue( - throwError('test error: such email does not exist!') + throwError(() => 'test error: such email does not exist!') ); cdcLoginService.login(); expect(cdcLoginService['busy$'].value).toBe(false); diff --git a/integration-libs/cdc/user-profile/forgot-password/cdc-forgot-password-component.service.spec.ts b/integration-libs/cdc/user-profile/forgot-password/cdc-forgot-password-component.service.spec.ts index 0add7068058..41dcd82ba4e 100644 --- a/integration-libs/cdc/user-profile/forgot-password/cdc-forgot-password-component.service.spec.ts +++ b/integration-libs/cdc/user-profile/forgot-password/cdc-forgot-password-component.service.spec.ts @@ -109,7 +109,9 @@ describe('CDCForgotPasswordComponentService', () => { cdcJsService.didLoad = createSpy().and.returnValue(of(true)); ( cdcJsService.resetPasswordWithoutScreenSet as jasmine.Spy - ).and.returnValue(throwError('test error: such email does not exist!')); + ).and.returnValue( + throwError(() => 'test error: such email does not exist!') + ); service.requestEmail(); expect(routingService.go).not.toHaveBeenCalled(); expect(service['busy$'].value).toBe(false); diff --git a/integration-libs/cdc/user-profile/register/cdc-register-component.service.spec.ts b/integration-libs/cdc/user-profile/register/cdc-register-component.service.spec.ts index fbd50323005..f65dad2f22d 100644 --- a/integration-libs/cdc/user-profile/register/cdc-register-component.service.spec.ts +++ b/integration-libs/cdc/user-profile/register/cdc-register-component.service.spec.ts @@ -25,7 +25,7 @@ import { UserRegisterFacade, UserSignUp, } from '@spartacus/user/profile/root'; -import { Observable, of, throwError } from 'rxjs'; +import { config, Observable, of, throwError } from 'rxjs'; import { CDCRegisterComponentService } from './cdc-register-component.service'; import createSpy = jasmine.createSpy; @@ -106,6 +106,22 @@ describe('CdcRegisterComponentService', () => { let fb: UntypedFormBuilder; let anonymousConsentsService: AnonymousConsentsService; + // TODO: CXSPA-4870 verify if can be avoided + let originalOnUnhandledError: ((err: any) => void) | null; + + beforeAll(() => { + // configure rxjs to not crash node instance with thrown errors + // TODO: CXSPA-4870 verify if can be avoided + originalOnUnhandledError = config.onUnhandledError; + config.onUnhandledError = () => {}; + }); + + afterAll(() => { + // reset rxjs configuration + // TODO: CXSPA-4870 verify if can be avoided + config.onUnhandledError = originalOnUnhandledError; + }); + beforeEach(() => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule], @@ -233,7 +249,7 @@ describe('CdcRegisterComponentService', () => { it('should not do anything when CDC registration fails', (done) => { cdcJsService.registerUserWithoutScreenSet = createSpy().and.returnValue( - throwError('ERROR') + throwError(() => 'ERROR') ); cdcUserRegisterService.generatePreferencesObject = createSpy().and.returnValue({}); diff --git a/integration-libs/cdc/user-profile/register/cdc-register-component.service.ts b/integration-libs/cdc/user-profile/register/cdc-register-component.service.ts index 46ef2a0fd0f..b6d4aafe6ca 100644 --- a/integration-libs/cdc/user-profile/register/cdc-register-component.service.ts +++ b/integration-libs/cdc/user-profile/register/cdc-register-component.service.ts @@ -89,7 +89,7 @@ export class CDCRegisterComponentService extends RegisterComponentService { */ register(user: UserSignUp): Observable { if (!user.firstName || !user.lastName || !user.uid || !user.password) { - return throwError(`The provided user is not valid: ${user}`); + return throwError(() => `The provided user is not valid: ${user}`); } /** fill the user preferences */ user.preferences = this.generatePreferencesObject(); diff --git a/integration-libs/cdc/user-profile/update-password/cdc-update-password-component.service.spec.ts b/integration-libs/cdc/user-profile/update-password/cdc-update-password-component.service.spec.ts index d90784aff73..485d39ba9f4 100644 --- a/integration-libs/cdc/user-profile/update-password/cdc-update-password-component.service.spec.ts +++ b/integration-libs/cdc/user-profile/update-password/cdc-update-password-component.service.spec.ts @@ -129,7 +129,10 @@ describe('CDCUpdatePasswordComponentService', () => { beforeEach(() => { cdcJsService.updateUserPasswordWithoutScreenSet = createSpy().and.returnValue( - throwError({ status: 'ERROR', errorDetails: 'Error occured' }) + throwError(() => ({ + status: 'ERROR', + errorDetails: 'Error occured', + })) ); TestBed.compileComponents(); }); diff --git a/integration-libs/cdc/user-profile/update-profile/cdc-update-profile-component.service.spec.ts b/integration-libs/cdc/user-profile/update-profile/cdc-update-profile-component.service.spec.ts index d069a3bdb07..4000dab7388 100644 --- a/integration-libs/cdc/user-profile/update-profile/cdc-update-profile-component.service.spec.ts +++ b/integration-libs/cdc/user-profile/update-profile/cdc-update-profile-component.service.spec.ts @@ -114,7 +114,10 @@ describe('UpdateProfileComponentService', () => { spyOn(globalMessageService, 'add'); service.form.patchValue(mockUser); cdcJsService.updateProfileWithoutScreenSet = createSpy().and.returnValue( - throwError({ status: 'ERROR', errorMessage: 'Error has occurred' }) + throwError(() => ({ + status: 'ERROR', + errorMessage: 'Error has occurred', + })) ); service.updateProfile(); diff --git a/integration-libs/cds/package.json b/integration-libs/cds/package.json index 03b56d2187b..1e191f7aa0e 100644 --- a/integration-libs/cds/package.json +++ b/integration-libs/cds/package.json @@ -34,7 +34,7 @@ "@spartacus/schematics": "6.6.0-1", "@spartacus/storefront": "6.6.0-1", "@spartacus/tracking": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/integration-libs/digital-payments/package.json b/integration-libs/digital-payments/package.json index 8ef5d7c91f5..5f42dd45e81 100644 --- a/integration-libs/digital-payments/package.json +++ b/integration-libs/digital-payments/package.json @@ -32,7 +32,7 @@ "@spartacus/core": "6.6.0-1", "@spartacus/schematics": "6.6.0-1", "@spartacus/storefront": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/integration-libs/epd-visualization/epd-visualization-api/adapters/storage-v1/storage-v1.adapter.ts b/integration-libs/epd-visualization/epd-visualization-api/adapters/storage-v1/storage-v1.adapter.ts index b9950773bd9..176ae50c343 100644 --- a/integration-libs/epd-visualization/epd-visualization-api/adapters/storage-v1/storage-v1.adapter.ts +++ b/integration-libs/epd-visualization/epd-visualization-api/adapters/storage-v1/storage-v1.adapter.ts @@ -21,7 +21,7 @@ import { EpdVisualizationInnerConfig, VisualizationApiConfig, } from '@spartacus/epd-visualization/root'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; /** @@ -98,9 +98,9 @@ export class StorageV1Adapter implements SceneAdapter { return this.http .get(this.getUrl(sceneId, nodeIds, $expand, $filter, contentType)) .pipe( - catchError((error) => - throwError(normalizeHttpError(error, this.logger)) - ), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), this.converter.pipeable(NODES_RESPONSE_NORMALIZER) ); } diff --git a/integration-libs/epd-visualization/epd-visualization-api/adapters/visualization-v1/visualization-v1.adapter.ts b/integration-libs/epd-visualization/epd-visualization-api/adapters/visualization-v1/visualization-v1.adapter.ts index 06166326ca9..7856fd3e51e 100644 --- a/integration-libs/epd-visualization/epd-visualization-api/adapters/visualization-v1/visualization-v1.adapter.ts +++ b/integration-libs/epd-visualization/epd-visualization-api/adapters/visualization-v1/visualization-v1.adapter.ts @@ -22,7 +22,7 @@ import { UsageId, VisualizationApiConfig, } from '@spartacus/epd-visualization/root'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; /** @@ -79,7 +79,9 @@ export class VisualizationV1Adapter implements VisualizationAdapter { folderUsageId: UsageId ): Observable { return this.http.get(this.getUrl(visualizationUsageId, folderUsageId)).pipe( - catchError((error) => throwError(normalizeHttpError(error, this.logger))), + catchError((error) => { + throw normalizeHttpError(error, this.logger); + }), this.converter.pipeable(LOOKUP_VISUALIZATIONS_RESPONSE_NORMALIZER) ); } diff --git a/integration-libs/epd-visualization/package.json b/integration-libs/epd-visualization/package.json index 5f1a0946f51..2662cb764b6 100644 --- a/integration-libs/epd-visualization/package.json +++ b/integration-libs/epd-visualization/package.json @@ -44,7 +44,7 @@ "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", "bootstrap": "^4.6.2", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/integration-libs/s4om/package.json b/integration-libs/s4om/package.json index 8ed387c1be7..9b808e65cad 100644 --- a/integration-libs/s4om/package.json +++ b/integration-libs/s4om/package.json @@ -30,7 +30,7 @@ "@spartacus/schematics": "6.6.0-1", "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/integration-libs/segment-refs/package.json b/integration-libs/segment-refs/package.json index 83faa796621..6c982b3bf56 100644 --- a/integration-libs/segment-refs/package.json +++ b/integration-libs/segment-refs/package.json @@ -25,7 +25,7 @@ "@angular/core": "^15.2.9", "@spartacus/core": "6.6.0-1", "@spartacus/schematics": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/package-lock.json b/package-lock.json index 267a5236517..2ac9b898853 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,10 +38,10 @@ "i18next-http-backend": "^1.4.5", "i18next-resources-to-backend": "^1.1.2", "ngx-infinite-scroll": "^15.0.0", - "rxjs": "^6.6.0", - "ts-loader": "^9.4.4", - "tslib": "^2.6.2", - "zone.js": "~0.12.0" + "rxjs": "^7.8.0", + "ts-loader": "^9.3.1", + "tslib": "^2.4.0", + "zone.js": "~0.11.8" }, "devDependencies": { "@angular-builders/custom-webpack": "^15.0.0", @@ -95,7 +95,7 @@ "http-server": "^14.1.1", "i18n-lint": "^1.1.0", "jasmine-core": "~4.6.0", - "jasmine-marbles": "^0.6.0", + "jasmine-marbles": "^0.9.2", "jest": "^29.0.0", "jest-preset-angular": "^13.0.0", "jsonc-parser": "^3.2.0", @@ -106,7 +106,6 @@ "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "^2.0.0", "karma-junit-reporter": "^2.0.1", - "karma-parallel": "^0.3.1", "ng-packagr": "^15.1.2", "nx": "15.9.2", "parse5": "^6.0.1", @@ -177,6 +176,24 @@ "yarn": ">= 1.13.0" } }, + "node_modules/@angular-devkit/architect/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "devOptional": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/architect/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "devOptional": true + }, "node_modules/@angular-devkit/build-angular": { "version": "15.2.9", "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-15.2.9.tgz", @@ -422,6 +439,24 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/@angular-devkit/build-angular/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, "node_modules/@angular-devkit/build-angular/node_modules/schema-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", @@ -512,6 +547,24 @@ "webpack-dev-server": "^4.0.0" } }, + "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, "node_modules/@angular-devkit/core": { "version": "15.2.9", "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.2.9.tgz", @@ -537,6 +590,22 @@ } } }, + "node_modules/@angular-devkit/core/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/core/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/@angular-devkit/schematics": { "version": "15.2.9", "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-15.2.9.tgz", @@ -554,6 +623,22 @@ "yarn": ">= 1.13.0" } }, + "node_modules/@angular-devkit/schematics/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/@angular-eslint/builder": { "version": "15.2.1", "dev": true, @@ -3417,6 +3502,24 @@ "@angular-devkit/build-angular": "^15.0.0" } }, + "node_modules/@nguniversal/builders/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@nguniversal/builders/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, "node_modules/@nguniversal/common": { "version": "15.2.0", "license": "MIT", @@ -5575,6 +5678,24 @@ "webpack": "^5.0.0" } }, + "node_modules/@nrwl/webpack/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@nrwl/webpack/node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, "node_modules/@nrwl/webpack/node_modules/sass-loader": { "version": "12.6.0", "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", @@ -5715,6 +5836,24 @@ "node": "*" } }, + "node_modules/@nrwl/workspace/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@nrwl/workspace/node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, "node_modules/@nrwl/workspace/node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", @@ -7417,15 +7556,6 @@ "ajv": "^8.8.2" } }, - "node_modules/amdefine": { - "version": "1.0.1", - "dev": true, - "license": "BSD-3-Clause OR MIT", - "optional": true, - "engines": { - "node": ">=0.4.2" - } - }, "node_modules/angular-oauth2-oidc": { "version": "15.0.1", "license": "MIT", @@ -7632,11 +7762,6 @@ "node": ">=8" } }, - "node_modules/async": { - "version": "1.5.2", - "dev": true, - "license": "MIT" - }, "node_modules/async-each-series": { "version": "0.1.1", "dev": true, @@ -8874,14 +8999,6 @@ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" } }, - "node_modules/concurrently/node_modules/rxjs": { - "version": "7.8.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/concurrently/node_modules/supports-color": { "version": "8.1.1", "dev": true, @@ -12091,34 +12208,6 @@ "dev": true, "license": "MIT" }, - "node_modules/handlebars": { - "version": "4.7.7", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/handlebars/node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/hard-rejection": { "version": "2.1.0", "dev": true, @@ -12916,14 +13005,6 @@ "node": ">=12.0.0" } }, - "node_modules/inquirer/node_modules/rxjs": { - "version": "7.8.0", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/internal-slot": { "version": "1.0.5", "dev": true, @@ -13535,30 +13616,6 @@ "node": ">=0.10.0" } }, - "node_modules/istanbul": { - "version": "0.4.5", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "abbrev": "1.0.x", - "async": "1.x", - "escodegen": "1.8.x", - "esprima": "2.7.x", - "glob": "^5.0.15", - "handlebars": "^4.0.1", - "js-yaml": "3.x", - "mkdirp": "0.5.x", - "nopt": "3.x", - "once": "1.x", - "resolve": "1.1.x", - "supports-color": "^3.1.0", - "which": "^1.1.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "istanbul": "lib/cli.js" - } - }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", "dev": true, @@ -13636,153 +13693,6 @@ "node": ">=8" } }, - "node_modules/istanbul/node_modules/escodegen": { - "version": "1.8.1", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esprima": "^2.7.1", - "estraverse": "^1.9.1", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=0.12.0" - }, - "optionalDependencies": { - "source-map": "~0.2.0" - } - }, - "node_modules/istanbul/node_modules/esprima": { - "version": "2.7.3", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul/node_modules/estraverse": { - "version": "1.9.3", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul/node_modules/glob": { - "version": "5.0.15", - "dev": true, - "license": "ISC", - "dependencies": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/istanbul/node_modules/has-flag": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul/node_modules/levn": { - "version": "0.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/istanbul/node_modules/optionator": { - "version": "0.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/istanbul/node_modules/prelude-ls": { - "version": "1.1.2", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/istanbul/node_modules/resolve": { - "version": "1.1.7", - "dev": true, - "license": "MIT" - }, - "node_modules/istanbul/node_modules/source-map": { - "version": "0.2.0", - "dev": true, - "optional": true, - "dependencies": { - "amdefine": ">=0.0.4" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/istanbul/node_modules/supports-color": { - "version": "3.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^1.0.0" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/istanbul/node_modules/type-check": { - "version": "0.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/istanbul/node_modules/which": { - "version": "1.3.1", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, "node_modules/jackspeak": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.1.0.tgz", @@ -13842,14 +13752,15 @@ "license": "MIT" }, "node_modules/jasmine-marbles": { - "version": "0.6.0", + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/jasmine-marbles/-/jasmine-marbles-0.9.2.tgz", + "integrity": "sha512-T7RjG4fRsdiGGzbQZ6Kj39qYt6O1/KIcR4FkUNsD3DUGkd/AzpwzN+xtk0DXlLWEz5BaVdK1SzMgQDVw879c4Q==", "dev": true, - "license": "MIT", "dependencies": { - "lodash": "^4.5.0" + "lodash": "^4.17.20" }, "peerDependencies": { - "rxjs": "^6.4.0" + "rxjs": "^7.0.0" } }, "node_modules/jest": { @@ -14803,21 +14714,6 @@ "karma": ">=0.9" } }, - "node_modules/karma-parallel": { - "version": "0.3.1", - "dev": true, - "license": "MIT", - "dependencies": { - "istanbul": "^0.4.5", - "lodash": "^4.17.11" - }, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "karma": ">= 1.0.0" - } - }, "node_modules/karma-source-map-support": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", @@ -16343,14 +16239,6 @@ "node": ">=10" } }, - "node_modules/ng-packagr/node_modules/rxjs": { - "version": "7.8.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/ngx-infinite-scroll": { "version": "15.0.0", "license": "MIT", @@ -16511,17 +16399,6 @@ "node": ">=0.4.0" } }, - "node_modules/nopt": { - "version": "3.0.6", - "dev": true, - "license": "ISC", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - } - }, "node_modules/normalize-package-data": { "version": "5.0.0", "devOptional": true, @@ -19179,19 +19056,13 @@ "license": "Apache-2.0" }, "node_modules/rxjs": { - "version": "6.6.7", - "license": "Apache-2.0", + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" + "tslib": "^2.1.0" } }, - "node_modules/rxjs/node_modules/tslib": { - "version": "1.14.1", - "license": "0BSD" - }, "node_modules/safe-buffer": { "version": "5.2.1", "funding": [ @@ -21485,18 +21356,6 @@ "node": "*" } }, - "node_modules/uglify-js": { - "version": "3.17.4", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/unbox-primitive": { "version": "1.0.2", "dev": true, @@ -22382,11 +22241,6 @@ "node": ">=0.10.0" } }, - "node_modules/wordwrap": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, "node_modules/wrap-ansi": { "version": "7.0.0", "devOptional": true, @@ -22569,6 +22423,14 @@ "type": "github", "url": "https://github.com/sponsors/wooorm" } + }, + "projects/eslint": { + "name": "@spartacus/eslint-plugin-eslint", + "version": "0.0.0-dev", + "extraneous": true, + "dependencies": { + "tslib": "^2.4.0" + } } } } diff --git a/package.json b/package.json index 43e3b3d223a..686691af674 100644 --- a/package.json +++ b/package.json @@ -130,7 +130,7 @@ "i18next-http-backend": "^1.4.5", "i18next-resources-to-backend": "^1.1.2", "ngx-infinite-scroll": "^15.0.0", - "rxjs": "^6.6.0", + "rxjs": "^7.8.0", "ts-loader": "^9.4.4", "tslib": "^2.6.2", "zone.js": "~0.12.0" @@ -187,7 +187,7 @@ "http-server": "^14.1.1", "i18n-lint": "^1.1.0", "jasmine-core": "~4.6.0", - "jasmine-marbles": "^0.6.0", + "jasmine-marbles": "^0.9.2", "jest": "^29.0.0", "jest-preset-angular": "^13.0.0", "jsonc-parser": "^3.2.0", @@ -198,7 +198,6 @@ "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "^2.0.0", "karma-junit-reporter": "^2.0.1", - "karma-parallel": "^0.3.1", "ng-packagr": "^15.1.2", "nx": "15.9.2", "parse5": "^6.0.1", diff --git a/projects/core/karma.conf.js b/projects/core/karma.conf.js index 9a5b7b752c0..d2d9195a15d 100644 --- a/projects/core/karma.conf.js +++ b/projects/core/karma.conf.js @@ -4,9 +4,8 @@ module.exports = function (config) { config.set({ basePath: '', - frameworks: ['parallel', 'jasmine', '@angular-devkit/build-angular'], + frameworks: ['jasmine', '@angular-devkit/build-angular'], plugins: [ - require('karma-parallel'), require('karma-jasmine'), require('karma-coverage'), require('karma-chrome-launcher'), @@ -14,10 +13,6 @@ module.exports = function (config) { require('@angular-devkit/build-angular/plugins/karma'), require('karma-junit-reporter'), ], - parallelOptions: { - executors: 2, - shardStrategy: 'round-robin', - }, client: { clearContext: false, // leave Jasmine Spec Runner output visible in browser jasmine: { diff --git a/projects/core/package.json b/projects/core/package.json index d2d9c28342d..41035454d30 100644 --- a/projects/core/package.json +++ b/projects/core/package.json @@ -26,7 +26,7 @@ "i18next": "^21.10.0", "i18next-http-backend": "^1.4.5", "i18next-resources-to-backend": "^1.1.2", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public" diff --git a/projects/core/src/auth/client-auth/http-interceptors/client-token.interceptor.ts b/projects/core/src/auth/client-auth/http-interceptors/client-token.interceptor.ts index a4e63c41f28..9630be21372 100644 --- a/projects/core/src/auth/client-auth/http-interceptors/client-token.interceptor.ts +++ b/projects/core/src/auth/client-auth/http-interceptors/client-token.interceptor.ts @@ -12,7 +12,7 @@ import { HttpRequest, } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Observable, of, throwError } from 'rxjs'; +import { Observable, of } from 'rxjs'; import { catchError, switchMap, take } from 'rxjs/operators'; import { OccEndpointsService } from '../../../occ/services/occ-endpoints.service'; import { @@ -73,7 +73,7 @@ export class ClientTokenInterceptor implements HttpInterceptor { next ); } - return throwError(errResponse); + throw errResponse; }) ); }) diff --git a/projects/core/src/auth/user-auth/facade/auth.service.ts b/projects/core/src/auth/user-auth/facade/auth.service.ts index 4056a35ee5d..9feccd4cf36 100644 --- a/projects/core/src/auth/user-auth/facade/auth.service.ts +++ b/projects/core/src/auth/user-auth/facade/auth.service.ts @@ -6,7 +6,7 @@ import { Injectable } from '@angular/core'; import { Store } from '@ngrx/store'; -import { BehaviorSubject, Observable } from 'rxjs'; +import { BehaviorSubject, Observable, lastValueFrom } from 'rxjs'; import { distinctUntilChanged, map } from 'rxjs/operators'; import { OCC_USER_ID_CURRENT } from '../../../occ/utils/occ-constants'; import { RoutingService } from '../../../routing/facade/routing.service'; @@ -91,9 +91,9 @@ export class AuthService { let uid = userId; if (this.authMultisiteIsolationService) { - uid = await this.authMultisiteIsolationService - .decorateUserId(uid) - .toPromise(); + uid = await lastValueFrom( + this.authMultisiteIsolationService.decorateUserId(uid) + ); } try { diff --git a/projects/core/src/auth/user-auth/http-interceptors/auth.interceptor.ts b/projects/core/src/auth/user-auth/http-interceptors/auth.interceptor.ts index d1caf4fc805..3a521ba7619 100644 --- a/projects/core/src/auth/user-auth/http-interceptors/auth.interceptor.ts +++ b/projects/core/src/auth/user-auth/http-interceptors/auth.interceptor.ts @@ -12,7 +12,7 @@ import { HttpRequest, } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { EMPTY, Observable, of, throwError } from 'rxjs'; +import { EMPTY, Observable, of } from 'rxjs'; import { catchError, map, switchMap, take } from 'rxjs/operators'; import { AuthConfigService } from '../services/auth-config.service'; import { AuthHttpHeaderService } from '../services/auth-http-header.service'; @@ -80,7 +80,7 @@ export class AuthInterceptor implements HttpInterceptor { } break; } - return throwError(errResponse); + throw errResponse; }) ) ) diff --git a/projects/core/src/auth/user-auth/user-auth.module.ts b/projects/core/src/auth/user-auth/user-auth.module.ts index 2b7954d93ea..414ca43f694 100644 --- a/projects/core/src/auth/user-auth/user-auth.module.ts +++ b/projects/core/src/auth/user-auth/user-auth.module.ts @@ -7,6 +7,7 @@ import { CommonModule } from '@angular/common'; import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core'; import { OAuthModule, OAuthStorage } from 'angular-oauth2-oidc'; +import { lastValueFrom } from 'rxjs'; import { switchMap } from 'rxjs/operators'; import { ConfigInitializerService } from '../../config/config-initializer/config-initializer.service'; import { provideDefaultConfig } from '../../config/config-providers'; @@ -26,18 +27,15 @@ export function checkOAuthParamsInUrl( authService: AuthService, configInit: ConfigInitializerService ): () => Promise { - const result = () => - configInit - .getStable() - .pipe( + return () => + lastValueFrom( + configInit.getStable().pipe( switchMap(() => // Wait for stable config is used, because with auth redirect would kick so quickly that the page would not be loaded correctly authService.checkOAuthParamsInUrl() ) ) - .toPromise(); - - return result; + ); } export function authStatePersistenceFactory( diff --git a/projects/core/src/cms/connectors/page/cms-page.connector.ts b/projects/core/src/cms/connectors/page/cms-page.connector.ts index aff57f6028c..581a1febc17 100644 --- a/projects/core/src/cms/connectors/page/cms-page.connector.ts +++ b/projects/core/src/cms/connectors/page/cms-page.connector.ts @@ -6,7 +6,7 @@ import { HttpErrorResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Observable, of, throwError } from 'rxjs'; +import { Observable, of } from 'rxjs'; import { catchError, switchMap } from 'rxjs/operators'; import { PageContext } from '../../../routing/models/page-context.model'; import { CmsStructureModel } from '../../model/page.model'; @@ -41,7 +41,7 @@ export class CmsPageConnector { ) { return of({}); } else { - return throwError(error); + throw error; } }) ); diff --git a/projects/core/src/cms/page/routing/default-route-page-meta.resolver.spec.ts b/projects/core/src/cms/page/routing/default-route-page-meta.resolver.spec.ts index 93d5c92eb9c..9c0c60bac33 100644 --- a/projects/core/src/cms/page/routing/default-route-page-meta.resolver.spec.ts +++ b/projects/core/src/cms/page/routing/default-route-page-meta.resolver.spec.ts @@ -1,6 +1,5 @@ import { TestBed } from '@angular/core/testing'; -import { Observable, of } from 'rxjs'; -import { take } from 'rxjs/operators'; +import { firstValueFrom, Observable, of } from 'rxjs'; import { TranslationService } from '../../../i18n/translation.service'; import { DefaultRoutePageMetaResolver } from './default-route-page-meta.resolver'; @@ -25,13 +24,12 @@ describe('DefaultRouteBreadcrumbResolver', () => { describe(`resolveBreadcrumbs`, () => { it('should emit breadcrumb with given path and i18n key (as string)', async () => { expect( - await resolver - .resolveBreadcrumbs({ + await firstValueFrom( + resolver.resolveBreadcrumbs({ url: '/testPath', pageMetaConfig: { breadcrumb: 'test.key' }, }) - .pipe(take(1)) - .toPromise() + ) ).toEqual([ { link: '/testPath', @@ -42,13 +40,12 @@ describe('DefaultRouteBreadcrumbResolver', () => { it('should emit breadcrumb with given path and i18n key (as object property)', async () => { expect( - await resolver - .resolveBreadcrumbs({ + await firstValueFrom( + resolver.resolveBreadcrumbs({ url: '/testPath', pageMetaConfig: { breadcrumb: { i18n: 'test.key' } }, }) - .pipe(take(1)) - .toPromise() + ) ).toEqual([ { link: '/testPath', @@ -59,13 +56,12 @@ describe('DefaultRouteBreadcrumbResolver', () => { it('should emit breadcrumb with given path and raw text', async () => { expect( - await resolver - .resolveBreadcrumbs({ + await firstValueFrom( + resolver.resolveBreadcrumbs({ url: '/testPath', pageMetaConfig: { breadcrumb: { raw: 'raw test' } }, }) - .pipe(take(1)) - .toPromise() + ) ).toEqual([ { link: '/testPath', diff --git a/projects/core/src/cms/page/routing/routing-page-meta.resolver.spec.ts b/projects/core/src/cms/page/routing/routing-page-meta.resolver.spec.ts index 63351fe0260..737a76ecb44 100644 --- a/projects/core/src/cms/page/routing/routing-page-meta.resolver.spec.ts +++ b/projects/core/src/cms/page/routing/routing-page-meta.resolver.spec.ts @@ -1,8 +1,7 @@ import { Injectable } from '@angular/core'; import { TestBed } from '@angular/core/testing'; import { ActivatedRouteSnapshot } from '@angular/router'; -import { BehaviorSubject, of } from 'rxjs'; -import { take } from 'rxjs/operators'; +import { BehaviorSubject, firstValueFrom, of } from 'rxjs'; import { ActivatedRoutesService } from '../../../routing/services/activated-routes.service'; import { DefaultRoutePageMetaResolver } from './default-route-page-meta.resolver'; import { ActivatedRouteSnapshotWithPageMeta } from './route-page-meta.model'; @@ -61,7 +60,7 @@ describe('RoutingPageMetaResolver', () => { { url: [{ path: 'child' }] }, ] as ActivatedRouteSnapshot[]); - const result = await resolver['routes$'].pipe(take(1)).toPromise(); + const result = await firstValueFrom(resolver['routes$']); expect(result).toEqual([ { url: [{ path: 'parent' }] }, { url: [{ path: 'child' }] }, @@ -77,9 +76,7 @@ describe('RoutingPageMetaResolver', () => { { url: [{ path: 'child' }] }, ] as ActivatedRouteSnapshotWithPageMeta[]); - const result = await resolver['routesWithExtras$'] - .pipe(take(1)) - .toPromise(); + const result = await firstValueFrom(resolver['routesWithExtras$']); expect(result).toEqual([ jasmine.objectContaining({ route: { url: [{ path: 'parent' }] } }), jasmine.objectContaining({ route: { url: [{ path: 'child' }] } }), @@ -94,9 +91,7 @@ describe('RoutingPageMetaResolver', () => { { url: [{ path: 'child' }] }, ] as ActivatedRouteSnapshotWithPageMeta[]); - const result = await resolver['routesWithExtras$'] - .pipe(take(1)) - .toPromise(); + const result = await firstValueFrom(resolver['routesWithExtras$']); expect(result).toEqual([ jasmine.objectContaining({ url: '/grandparent' }), jasmine.objectContaining({ url: '/grandparent/test/parent' }), @@ -125,9 +120,7 @@ describe('RoutingPageMetaResolver', () => { const resolverInstanceB = TestBed.inject(ResolverB); const resolverInstanceC = TestBed.inject(ResolverC); - const result = await resolver['routesWithExtras$'] - .pipe(take(1)) - .toPromise(); + const result = await firstValueFrom(resolver['routesWithExtras$']); expect(result).toEqual([ jasmine.objectContaining({ resolver: resolverInstanceA }), @@ -155,9 +148,7 @@ describe('RoutingPageMetaResolver', () => { const resolverInstanceA = TestBed.inject(ResolverA); const resolverInstanceC = TestBed.inject(ResolverC); - const result = await resolver['routesWithExtras$'] - .pipe(take(1)) - .toPromise(); + const result = await firstValueFrom(resolver['routesWithExtras$']); expect(result).toEqual([ jasmine.objectContaining({ resolver: resolverInstanceA }), @@ -181,9 +172,7 @@ describe('RoutingPageMetaResolver', () => { DefaultRoutePageMetaResolver ); - const result = await resolver['routesWithExtras$'] - .pipe(take(1)) - .toPromise(); + const result = await firstValueFrom(resolver['routesWithExtras$']); expect(result).toEqual([ jasmine.objectContaining({ resolver: defaultResolverInstance }), @@ -211,10 +200,7 @@ describe('RoutingPageMetaResolver', () => { { url: [] }, // root route ] as ActivatedRouteSnapshotWithPageMeta[]); - const result = await resolver - .resolveBreadcrumbs() - .pipe(take(1)) - .toPromise(); + const result = await firstValueFrom(resolver.resolveBreadcrumbs()); expect(result).toEqual([]); expect(defaultResolver.resolveBreadcrumbs).not.toHaveBeenCalled(); }); @@ -230,9 +216,7 @@ describe('RoutingPageMetaResolver', () => { }, ] as ActivatedRouteSnapshotWithPageMeta[]); - expect( - await resolver.resolveBreadcrumbs().pipe(take(1)).toPromise() - ).toEqual([]); + expect(await firstValueFrom(resolver.resolveBreadcrumbs())).toEqual([]); expect(defaultResolver.resolveBreadcrumbs).not.toHaveBeenCalled(); }); @@ -250,9 +234,7 @@ describe('RoutingPageMetaResolver', () => { }, ] as ActivatedRouteSnapshotWithPageMeta[]); - expect( - await resolver.resolveBreadcrumbs().pipe(take(1)).toPromise() - ).toEqual([]); + expect(await firstValueFrom(resolver.resolveBreadcrumbs())).toEqual([]); expect(defaultResolver.resolveBreadcrumbs).not.toHaveBeenCalled(); }); @@ -281,9 +263,7 @@ describe('RoutingPageMetaResolver', () => { mockActivatedRoutes$.next(testRoutes); - expect( - await resolver.resolveBreadcrumbs().pipe(take(1)).toPromise() - ).toEqual([ + expect(await firstValueFrom(resolver.resolveBreadcrumbs())).toEqual([ { link: '/grandparent', label: 'grandparent.breadcrumb' }, { link: '/grandparent/parent', label: 'parent.breadcrumb' }, ]); @@ -327,9 +307,7 @@ describe('RoutingPageMetaResolver', () => { mockActivatedRoutes$.next(testRoutes); - expect( - await resolver.resolveBreadcrumbs().pipe(take(1)).toPromise() - ).toEqual([ + expect(await firstValueFrom(resolver.resolveBreadcrumbs())).toEqual([ { link: '/grandparent', label: 'grandparent.breadcrumb' }, { link: '/grandparent/parent', label: 'parent.breadcrumb' }, ]); @@ -362,10 +340,9 @@ describe('RoutingPageMetaResolver', () => { mockActivatedRoutes$.next(testRoutes); expect( - await resolver - .resolveBreadcrumbs({ includeCurrentRoute: true }) - .pipe(take(1)) - .toPromise() + await firstValueFrom( + resolver.resolveBreadcrumbs({ includeCurrentRoute: true }) + ) ).toEqual([ { link: '/grandparent', label: 'grandparent.breadcrumb' }, { link: '/grandparent/parent', label: 'parent.breadcrumb' }, diff --git a/projects/core/src/cms/store/effects/page.effect.spec.ts b/projects/core/src/cms/store/effects/page.effect.spec.ts index 92a4446bb22..fde6705e3f1 100644 --- a/projects/core/src/cms/store/effects/page.effect.spec.ts +++ b/projects/core/src/cms/store/effects/page.effect.spec.ts @@ -1,7 +1,9 @@ +import { HttpErrorResponse } from '@angular/common/http'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; import { provideMockActions } from '@ngrx/effects/testing'; import { Action, StoreModule } from '@ngrx/store'; +import { normalizeHttpError } from '@spartacus/core'; import { cold, hot } from 'jasmine-marbles'; import { Observable, of, throwError } from 'rxjs'; import { AuthActions } from '../../../auth/user-auth/store/actions/index'; @@ -14,8 +16,6 @@ import { CmsStructureModel, Page } from '../../model/page.model'; import { CmsActions } from '../actions/index'; import { CMS_FEATURE } from '../cms-state'; import * as fromEffects from './page.effect'; -import { HttpErrorResponse } from '@angular/common/http'; -import { normalizeHttpError } from '@spartacus/core'; function mockDateNow(): number { return 1000000000000; @@ -137,7 +137,9 @@ describe('Page Effects', () => { it('should dispatch LoadPageDataFail action', () => { const error = new HttpErrorResponse({ error: 'error' }); - spyOn(cmsPageConnector, 'get').and.returnValue(throwError(error)); + spyOn(cmsPageConnector, 'get').and.returnValue( + throwError(() => error) + ); const action = new CmsActions.LoadCmsPageData(pageContext); const completion = new CmsActions.LoadCmsPageDataFail( diff --git a/projects/core/src/config/config-initializer/config-initializer.module.ts b/projects/core/src/config/config-initializer/config-initializer.module.ts index 0e725439a40..4142078535b 100644 --- a/projects/core/src/config/config-initializer/config-initializer.module.ts +++ b/projects/core/src/config/config-initializer/config-initializer.module.ts @@ -11,11 +11,12 @@ import { NgModule, Optional, } from '@angular/core'; +import { lastValueFrom } from 'rxjs'; import { Config } from '../config-tokens'; import { - ConfigInitializer, CONFIG_INITIALIZER, CONFIG_INITIALIZER_FORROOT_GUARD, + ConfigInitializer, } from './config-initializer'; import { ConfigInitializerService } from './config-initializer.service'; @@ -30,7 +31,7 @@ export function configInitializerFactory( export function locationInitializedFactory( configInitializer: ConfigInitializerService ): Promise { - return configInitializer.getStable().toPromise(); + return lastValueFrom(configInitializer.getStable()); } @NgModule({}) diff --git a/projects/core/src/config/services/configuration.service.spec.ts b/projects/core/src/config/services/configuration.service.spec.ts index 43e5b4865ac..f784ab04c46 100644 --- a/projects/core/src/config/services/configuration.service.spec.ts +++ b/projects/core/src/config/services/configuration.service.spec.ts @@ -8,7 +8,7 @@ import { provideConfig, provideDefaultConfig, } from '@spartacus/core'; -import { take } from 'rxjs/operators'; +import { firstValueFrom } from 'rxjs'; import { ConfigurationService } from './configuration.service'; @NgModule({ @@ -79,9 +79,7 @@ describe('ConfigurationService', () => { it('should contribute to general config', async () => { eventService.dispatch(moduleEvent); - const config: any = await service.unifiedConfig$ - .pipe(take(1)) - .toPromise(); + const config: any = await firstValueFrom(service.unifiedConfig$); expect(config.a).toEqual('default a'); expect(config.b).toEqual('module default b'); expect(config.c).toEqual('module c'); @@ -89,9 +87,7 @@ describe('ConfigurationService', () => { it('should favor root config over lazy config', async () => { eventService.dispatch(moduleEvent); - const config: any = await service.unifiedConfig$ - .pipe(take(1)) - .toPromise(); + const config: any = await firstValueFrom(service.unifiedConfig$); expect(config.d).toEqual('root d'); }); diff --git a/projects/core/src/global-message/http-interceptors/http-error.interceptor.spec.ts b/projects/core/src/global-message/http-interceptors/http-error.interceptor.spec.ts index 85dd10e1f77..f3229e1446b 100644 --- a/projects/core/src/global-message/http-interceptors/http-error.interceptor.spec.ts +++ b/projects/core/src/global-message/http-interceptors/http-error.interceptor.spec.ts @@ -105,7 +105,7 @@ describe('HttpErrorInterceptor', () => { it('should call handleError for ' + handlerClass.name, function () { http .get('/123') - .pipe(catchError((error: any) => throwError(error))) + .pipe(catchError((error: any) => throwError(() => error))) .subscribe({ error: (error) => (this.error = error), }); @@ -147,7 +147,7 @@ describe('HttpErrorInterceptor', () => { http .get('/validation-error') - .pipe(catchError((error: any) => throwError(error))) + .pipe(catchError((error: any) => throwError(() => error))) .subscribe({ error: (error) => ({ if(this) { @@ -172,7 +172,7 @@ describe('HttpErrorInterceptor', () => { spyOn(console, 'warn'); http .get('/unknown') - .pipe(catchError((error: any) => throwError(error))) + .pipe(catchError((error: any) => throwError(() => error))) .subscribe({ error: (error) => ({ if(this) { diff --git a/projects/core/src/global-message/http-interceptors/http-error.interceptor.ts b/projects/core/src/global-message/http-interceptors/http-error.interceptor.ts index 84f047f567c..62576cb3abe 100644 --- a/projects/core/src/global-message/http-interceptors/http-error.interceptor.ts +++ b/projects/core/src/global-message/http-interceptors/http-error.interceptor.ts @@ -12,8 +12,8 @@ import { HttpRequest, } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Observable, throwError } from 'rxjs'; -import { catchError, shareReplay } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { shareReplay, tap } from 'rxjs/operators'; import { UnifiedInjector } from '../../lazy-loading/unified-injector'; import { resolveApplicable } from '../../util/applicable'; import { getLastValueSync } from '../../util/rxjs/get-last-value-sync'; @@ -32,11 +32,12 @@ export class HttpErrorInterceptor implements HttpInterceptor { next: HttpHandler ): Observable> { return next.handle(request).pipe( - catchError((response: any) => { - if (response instanceof HttpErrorResponse) { - this.handleErrorResponse(request, response); - } - return throwError(response); + tap({ + error: (response: any) => { + if (response instanceof HttpErrorResponse) { + this.handleErrorResponse(request, response); + } + }, }) ); } diff --git a/projects/core/src/i18n/config/i18n-config-initializer.ts b/projects/core/src/i18n/config/i18n-config-initializer.ts index 7d343522742..7bd3e2cb927 100644 --- a/projects/core/src/i18n/config/i18n-config-initializer.ts +++ b/projects/core/src/i18n/config/i18n-config-initializer.ts @@ -5,7 +5,7 @@ */ import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; +import { Observable, lastValueFrom } from 'rxjs'; import { map } from 'rxjs/operators'; import { ConfigInitializer } from '../../config/config-initializer/config-initializer'; import { ConfigInitializerService } from '../../config/config-initializer/config-initializer.service'; @@ -14,7 +14,7 @@ import { I18nConfig } from './i18n-config'; @Injectable({ providedIn: 'root' }) export class I18nConfigInitializer implements ConfigInitializer { readonly scopes = ['i18n.fallbackLang']; - readonly configFactory = () => this.resolveConfig().toPromise(); + readonly configFactory = () => lastValueFrom(this.resolveConfig()); constructor(protected configInit: ConfigInitializerService) {} diff --git a/projects/core/src/i18n/i18next/i18next-providers.ts b/projects/core/src/i18n/i18next/i18next-providers.ts index 9a9a030bc91..fd104574d8c 100644 --- a/projects/core/src/i18n/i18next/i18next-providers.ts +++ b/projects/core/src/i18n/i18next/i18next-providers.ts @@ -5,6 +5,7 @@ */ import { APP_INITIALIZER, inject, Provider } from '@angular/core'; +import { lastValueFrom } from 'rxjs'; import { ConfigInitializerService } from '../../config/config-initializer/config-initializer.service'; import { i18nextBackendProviders } from './i18next-backend/i18next-backend.providers'; import { I18nextInitializer } from './i18next-initializer'; @@ -16,10 +17,9 @@ export const i18nextProviders: Provider[] = [ const configInitializerService = inject(ConfigInitializerService); const i18nextInitializer = inject(I18nextInitializer); return () => - configInitializerService - .getStable('i18n') - .toPromise() - .then(() => i18nextInitializer.initialize()); + lastValueFrom(configInitializerService.getStable('i18n')).then(() => + i18nextInitializer.initialize() + ); }, multi: true, }, diff --git a/projects/core/src/lazy-loading/facade-factory/facade-factory.service.spec.ts b/projects/core/src/lazy-loading/facade-factory/facade-factory.service.spec.ts index 1dadebf3fb9..cdf9a44ff3f 100644 --- a/projects/core/src/lazy-loading/facade-factory/facade-factory.service.spec.ts +++ b/projects/core/src/lazy-loading/facade-factory/facade-factory.service.spec.ts @@ -7,12 +7,13 @@ import { } from '@angular/core/testing'; import { BehaviorSubject, + firstValueFrom, isObservable, + lastValueFrom, Observable, of, Subscription, } from 'rxjs'; -import { take } from 'rxjs/operators'; import { CmsConfig } from '../../cms/config/cms-config'; import { EventService } from '../../event/event.service'; import { getLastValueSync } from '../../util/rxjs/get-last-value-sync'; @@ -128,12 +129,12 @@ describe('FacadeFactoryService', () => { describe('async option', () => { it('should not delay initialization if set to false', async () => { const facade = service.create(testFacadeDescriptor); - const result = await facade.testProperty2.pipe(take(1)).toPromise(); + const result = await firstValueFrom(facade.testProperty2); expect(result).toEqual(''); }); it('should delay initialization if set to true', async () => { const facade = service.create({ ...testFacadeDescriptor, async: true }); - const result = await facade.testProperty2.pipe(take(1)).toPromise(); + const result = await firstValueFrom(facade.testProperty2); expect(result).toEqual('async initialized'); }); }); @@ -164,7 +165,7 @@ describe('FacadeFactoryService', () => { tick(); // to finish running timers in the test implementation })); it('should proxy return observable from the method', async () => { - const result = await facade.testMethod('a', 1).toPromise(); + const result = await lastValueFrom(facade.testMethod('a', 1)); expect(result).toEqual('a1'); }); it('should call the method logic without subscribing', fakeAsync(() => { @@ -184,12 +185,12 @@ describe('FacadeFactoryService', () => { expect(moduleInitializedEvent).toBeUndefined(); })); it('should trigger lazy load on subscribe', async () => { - await facade.testProperty.toPromise(); + await lastValueFrom(facade.testProperty); expect(moduleInitializedEvent).toBeDefined(); expect(moduleInitializedEvent.feature).toEqual(TEST_FEATURE_NAME); }); it('should proxy return observable from the property', async () => { - const result = await facade.testProperty.toPromise(); + const result = await lastValueFrom(facade.testProperty); expect(result).toEqual(333); }); }); diff --git a/projects/core/src/lazy-loading/facade-factory/facade-factory.service.ts b/projects/core/src/lazy-loading/facade-factory/facade-factory.service.ts index 703a0d8f22c..bd53c42010a 100644 --- a/projects/core/src/lazy-loading/facade-factory/facade-factory.service.ts +++ b/projects/core/src/lazy-loading/facade-factory/facade-factory.service.ts @@ -6,19 +6,14 @@ import { AbstractType, Injectable, Injector } from '@angular/core'; import { - ConnectableObservable, EMPTY, - isObservable, Observable, + ReplaySubject, + connectable, + isObservable, throwError, } from 'rxjs'; -import { - delay, - map, - publishReplay, - shareReplay, - switchMap, -} from 'rxjs/operators'; +import { delay, map, shareReplay, switchMap } from 'rxjs/operators'; import { FeatureModulesService } from '../feature-modules.service'; import { FacadeDescriptor } from './facade-descriptor'; @@ -47,7 +42,7 @@ export class FacadeFactoryService { ): Observable { if (!this.featureModules.isConfigured(feature)) { return throwError( - new Error(`Feature ${feature} is not configured properly`) + () => new Error(`Feature ${feature} is not configured properly`) ); } @@ -77,11 +72,14 @@ export class FacadeFactoryService { method: string, args: unknown[] ): Observable { - const callResult$ = resolver$.pipe( - map((service) => service[method](...args)), - publishReplay() + const callResult$ = connectable( + resolver$.pipe(map((service) => service[method](...args))), + { + connector: () => new ReplaySubject(), + resetOnDisconnect: false, + } ); - (callResult$ as ConnectableObservable).connect(); + callResult$.connect(); return callResult$.pipe( switchMap((result) => { diff --git a/projects/core/src/lazy-loading/feature-modules.service.ts b/projects/core/src/lazy-loading/feature-modules.service.ts index c81cff39cff..a1d0efac945 100644 --- a/projects/core/src/lazy-loading/feature-modules.service.ts +++ b/projects/core/src/lazy-loading/feature-modules.service.ts @@ -46,7 +46,8 @@ export class FeatureModulesService { if (!this.features.has(featureName)) { if (!this.isConfigured(featureName)) { return throwError( - new Error('No module defined for Feature Module ' + featureName) + () => + new Error('No module defined for Feature Module ' + featureName) ); } diff --git a/projects/core/src/lazy-loading/lazy-modules.service.ts b/projects/core/src/lazy-loading/lazy-modules.service.ts index d807d1482a4..bae662a54f9 100644 --- a/projects/core/src/lazy-loading/lazy-modules.service.ts +++ b/projects/core/src/lazy-loading/lazy-modules.service.ts @@ -14,24 +14,17 @@ import { inject, } from '@angular/core'; import { - ConnectableObservable, + Connectable, Observable, + ReplaySubject, Subscription, combineLatest, + connectable, from, of, queueScheduler, - throwError, } from 'rxjs'; -import { - catchError, - concatMap, - map, - observeOn, - publishReplay, - switchMap, - tap, -} from 'rxjs/operators'; +import { concatMap, map, observeOn, switchMap, tap } from 'rxjs/operators'; import { EventService } from '../event/event.service'; import { LoggerService } from '../logger'; import { CombinedInjector } from '../util/combined-injector'; @@ -51,12 +44,15 @@ export class LazyModulesService implements OnDestroy { /** * Expose lazy loaded module references */ - readonly modules$: Observable> = this.events - .get(ModuleInitializedEvent) - .pipe( - map((event) => event.moduleRef), - publishReplay() - ); + readonly modules$: Observable> = connectable( + this.events + .get(ModuleInitializedEvent) + .pipe(map((event) => event.moduleRef)), + { + connector: () => new ReplaySubject(), + resetOnDisconnect: false, + } + ); private readonly dependencyModules = new Map>(); private readonly eventSubscription: Subscription; @@ -67,7 +63,7 @@ export class LazyModulesService implements OnDestroy { protected events: EventService ) { this.eventSubscription = ( - this.modules$ as ConnectableObservable> + this.modules$ as Connectable> ).connect(); } @@ -163,12 +159,13 @@ export class LazyModulesService implements OnDestroy { this.runModuleInitializerFunctions(moduleInits); if (asyncInitPromises.length) { return from(Promise.all(asyncInitPromises)).pipe( - catchError((error) => { - this.logger.error( - 'MODULE_INITIALIZER promise was rejected while lazy loading a module.', - error - ); - return throwError(error); + tap({ + error: (error) => { + this.logger.error( + 'MODULE_INITIALIZER promise was rejected while lazy loading a module.', + error + ); + }, }), switchMap(() => of(moduleRef)) ); diff --git a/projects/core/src/occ/adapters/user/occ-anonymous-consent-templates.adapter.ts b/projects/core/src/occ/adapters/user/occ-anonymous-consent-templates.adapter.ts index d98656acf50..0b1daf10e89 100644 --- a/projects/core/src/occ/adapters/user/occ-anonymous-consent-templates.adapter.ts +++ b/projects/core/src/occ/adapters/user/occ-anonymous-consent-templates.adapter.ts @@ -6,17 +6,18 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { AnonymousConsentTemplatesAdapter } from '../../../anonymous-consents/connectors/anonymous-consent-templates.adapter'; import { ANONYMOUS_CONSENT_NORMALIZER } from '../../../anonymous-consents/connectors/converters'; import { - AnonymousConsent, ANONYMOUS_CONSENTS_HEADER, + AnonymousConsent, ConsentTemplate, } from '../../../model/consent.model'; import { CONSENT_TEMPLATE_NORMALIZER } from '../../../user/connectors/consent/converters'; import { ConverterService } from '../../../util/converter.service'; +import { normalizeHttpError } from '../../../util/normalize-http-error'; import { Occ } from '../../occ-models/occ.models'; import { OccEndpointsService } from '../../services/occ-endpoints.service'; @@ -33,7 +34,9 @@ export class OccAnonymousConsentTemplatesAdapter loadAnonymousConsentTemplates(): Observable { const url = this.occEndpoints.buildUrl('anonymousConsentTemplates'); return this.http.get(url).pipe( - catchError((error) => throwError(error)), + catchError((error: any) => { + throw normalizeHttpError(error); + }), map((consentList) => consentList.consentTemplates ?? []), this.converter.pipeableMany(CONSENT_TEMPLATE_NORMALIZER) ); @@ -45,7 +48,9 @@ export class OccAnonymousConsentTemplatesAdapter return this.http .head(url, { observe: 'response' }) .pipe( - catchError((error) => throwError(error)), + catchError((error: any) => { + throw normalizeHttpError(error); + }), map( (response) => response.headers.get(ANONYMOUS_CONSENTS_HEADER) ?? '' ), diff --git a/projects/core/src/occ/adapters/user/occ-user-address.adapter.ts b/projects/core/src/occ/adapters/user/occ-user-address.adapter.ts index 77cda550c64..d31b42f9bf4 100644 --- a/projects/core/src/occ/adapters/user/occ-user-address.adapter.ts +++ b/projects/core/src/occ/adapters/user/occ-user-address.adapter.ts @@ -6,7 +6,7 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { Address, AddressValidation } from '../../../model/address.model'; import { @@ -16,6 +16,7 @@ import { } from '../../../user/connectors/address/converters'; import { UserAddressAdapter } from '../../../user/connectors/address/user-address.adapter'; import { ConverterService } from '../../../util/converter.service'; +import { normalizeHttpError } from '../../../util/normalize-http-error'; import { Occ } from '../../occ-models/occ.models'; import { OccEndpointsService } from '../../services/occ-endpoints.service'; import { @@ -43,7 +44,9 @@ export class OccUserAddressAdapter implements UserAddressAdapter { }); return this.http.get(url, { headers }).pipe( - catchError((error: any) => throwError(error)), + catchError((error: any) => { + throw normalizeHttpError(error); + }), map((addressList) => addressList.addresses ?? []), this.converter.pipeableMany(ADDRESS_NORMALIZER) ); @@ -58,9 +61,11 @@ export class OccUserAddressAdapter implements UserAddressAdapter { }); address = this.converter.convert(address, ADDRESS_SERIALIZER); - return this.http - .post(url, address, { headers }) - .pipe(catchError((error: any) => throwError(error))); + return this.http.post(url, address, { headers }).pipe( + catchError((error: any) => { + throw normalizeHttpError(error); + }) + ); } update(userId: string, addressId: string, address: Address): Observable<{}> { @@ -72,9 +77,11 @@ export class OccUserAddressAdapter implements UserAddressAdapter { }); address = this.converter.convert(address, ADDRESS_SERIALIZER); - return this.http - .patch(url, address, { headers }) - .pipe(catchError((error: any) => throwError(error))); + return this.http.patch(url, address, { headers }).pipe( + catchError((error: any) => { + throw normalizeHttpError(error); + }) + ); } verify(userId: string, address: Address): Observable { @@ -90,7 +97,9 @@ export class OccUserAddressAdapter implements UserAddressAdapter { address = this.converter.convert(address, ADDRESS_SERIALIZER); return this.http.post(url, address, { headers }).pipe( - catchError((error: any) => throwError(error)), + catchError((error: any) => { + throw normalizeHttpError(error); + }), this.converter.pipeable(ADDRESS_VALIDATION_NORMALIZER) ); } @@ -103,8 +112,10 @@ export class OccUserAddressAdapter implements UserAddressAdapter { ...CONTENT_TYPE_JSON_HEADER, }); - return this.http - .delete(url, { headers }) - .pipe(catchError((error: any) => throwError(error))); + return this.http.delete(url, { headers }).pipe( + catchError((error: any) => { + throw normalizeHttpError(error); + }) + ); } } diff --git a/projects/core/src/occ/adapters/user/occ-user-consent.adapter.ts b/projects/core/src/occ/adapters/user/occ-user-consent.adapter.ts index 0aec99198c3..d1af10d32dd 100644 --- a/projects/core/src/occ/adapters/user/occ-user-consent.adapter.ts +++ b/projects/core/src/occ/adapters/user/occ-user-consent.adapter.ts @@ -6,12 +6,13 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { ConsentTemplate } from '../../../model/consent.model'; import { CONSENT_TEMPLATE_NORMALIZER } from '../../../user/connectors/consent/converters'; import { UserConsentAdapter } from '../../../user/connectors/consent/user-consent.adapter'; import { ConverterService } from '../../../util/converter.service'; +import { normalizeHttpError } from '../../../util/normalize-http-error'; import { Occ } from '../../occ-models/occ.models'; import { OccEndpointsService } from '../../services/occ-endpoints.service'; @@ -29,7 +30,9 @@ export class OccUserConsentAdapter implements UserConsentAdapter { }); const headers = new HttpHeaders({ 'Cache-Control': 'no-cache' }); return this.http.get(url, { headers }).pipe( - catchError((error: any) => throwError(error)), + catchError((error: any) => { + throw normalizeHttpError(error); + }), map((consentList) => consentList.consentTemplates ?? []), this.converter.pipeableMany(CONSENT_TEMPLATE_NORMALIZER) ); @@ -53,7 +56,9 @@ export class OccUserConsentAdapter implements UserConsentAdapter { return this.http .post(url, httpParams, { headers }) .pipe( - catchError((error) => throwError(error)), + catchError((error: any) => { + throw normalizeHttpError(error); + }), this.converter.pipeable(CONSENT_TEMPLATE_NORMALIZER) ); } diff --git a/projects/core/src/occ/adapters/user/occ-user-interests.adapter.ts b/projects/core/src/occ/adapters/user/occ-user-interests.adapter.ts index 5507a9d53fb..66895d0dfa9 100644 --- a/projects/core/src/occ/adapters/user/occ-user-interests.adapter.ts +++ b/projects/core/src/occ/adapters/user/occ-user-interests.adapter.ts @@ -6,7 +6,7 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { forkJoin, Observable, throwError } from 'rxjs'; +import { Observable, forkJoin } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { NotificationType, @@ -16,6 +16,7 @@ import { import { PRODUCT_INTERESTS_NORMALIZER } from '../../../user/connectors/interests/converters'; import { UserInterestsAdapter } from '../../../user/connectors/interests/user-interests.adapter'; import { ConverterService } from '../../../util/converter.service'; +import { normalizeHttpError } from '../../../util/normalize-http-error'; import { OccConfig } from '../../config/occ-config'; import { OccEndpointsService } from '../../services/occ-endpoints.service'; @@ -66,7 +67,9 @@ export class OccUserInterestsAdapter implements UserInterestsAdapter { ) .pipe( this.converter.pipeable(PRODUCT_INTERESTS_NORMALIZER), - catchError((error: any) => throwError(error)) + catchError((error: any) => { + throw normalizeHttpError(error); + }) ); } @@ -89,7 +92,11 @@ export class OccUserInterestsAdapter implements UserInterestsAdapter { params: params, } ) - .pipe(catchError((error: any) => throwError(error))) + .pipe( + catchError((error: any) => { + throw normalizeHttpError(error); + }) + ) ); }); return forkJoin(r); @@ -114,6 +121,10 @@ export class OccUserInterestsAdapter implements UserInterestsAdapter { params, } ) - .pipe(catchError((error: any) => throwError(error))); + .pipe( + catchError((error: any) => { + throw normalizeHttpError(error); + }) + ); } } diff --git a/projects/core/src/occ/adapters/user/occ-user-notification-preference.adapter.ts b/projects/core/src/occ/adapters/user/occ-user-notification-preference.adapter.ts index 319c26fa7cd..77a763a6d3b 100644 --- a/projects/core/src/occ/adapters/user/occ-user-notification-preference.adapter.ts +++ b/projects/core/src/occ/adapters/user/occ-user-notification-preference.adapter.ts @@ -6,7 +6,7 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { NotificationPreference, @@ -18,6 +18,7 @@ import { } from '../../../user/connectors/notification-preference'; import { UserNotificationPreferenceAdapter } from '../../../user/connectors/notification-preference/user-notification-preference.adapter'; import { ConverterService } from '../../../util/converter.service'; +import { normalizeHttpError } from '../../../util/normalize-http-error'; import { OccEndpointsService } from '../../services/occ-endpoints.service'; const headers = new HttpHeaders({ @@ -47,7 +48,9 @@ export class OccUserNotificationPreferenceAdapter .pipe( map((list) => list.preferences ?? []), this.converter.pipeableMany(NOTIFICATION_PREFERENCE_NORMALIZER), - catchError((error: any) => throwError(error)) + catchError((error: any) => { + throw normalizeHttpError(error); + }) ); } @@ -67,6 +70,10 @@ export class OccUserNotificationPreferenceAdapter { preferences: preferences }, { headers } ) - .pipe(catchError((error: any) => throwError(error))); + .pipe( + catchError((error: any) => { + throw normalizeHttpError(error); + }) + ); } } diff --git a/projects/core/src/occ/adapters/user/occ-user-payment.adapter.ts b/projects/core/src/occ/adapters/user/occ-user-payment.adapter.ts index c1f4afb8505..34bb42e697f 100644 --- a/projects/core/src/occ/adapters/user/occ-user-payment.adapter.ts +++ b/projects/core/src/occ/adapters/user/occ-user-payment.adapter.ts @@ -6,12 +6,13 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { PAYMENT_DETAILS_NORMALIZER } from '../../../checkout/connectors/payment/converters'; import { PaymentDetails } from '../../../model/payment.model'; import { UserPaymentAdapter } from '../../../user/connectors/payment/user-payment.adapter'; import { ConverterService } from '../../../util/converter.service'; +import { normalizeHttpError } from '../../../util/normalize-http-error'; import { Occ } from '../../occ-models/occ.models'; import { OccEndpointsService } from '../../services/occ-endpoints.service'; @@ -35,7 +36,9 @@ export class OccUserPaymentAdapter implements UserPaymentAdapter { }); return this.http.get(url, { headers }).pipe( - catchError((error: any) => throwError(error)), + catchError((error: any) => { + throw normalizeHttpError(error); + }), map((methodList) => methodList.payments ?? []), this.converter.pipeableMany(PAYMENT_DETAILS_NORMALIZER) ); @@ -49,9 +52,11 @@ export class OccUserPaymentAdapter implements UserPaymentAdapter { ...CONTENT_TYPE_JSON_HEADER, }); - return this.http - .delete(url, { headers }) - .pipe(catchError((error: any) => throwError(error))); + return this.http.delete(url, { headers }).pipe( + catchError((error: any) => { + throw normalizeHttpError(error); + }) + ); } setDefault(userId: string, paymentMethodID: string): Observable<{}> { @@ -70,6 +75,10 @@ export class OccUserPaymentAdapter implements UserPaymentAdapter { { billingAddress: { titleCode: 'mr' }, defaultPayment: true }, { headers } ) - .pipe(catchError((error: any) => throwError(error))); + .pipe( + catchError((error: any) => { + throw normalizeHttpError(error); + }) + ); } } diff --git a/projects/core/src/occ/services/occ-requests-optimizer.service.spec.ts b/projects/core/src/occ/services/occ-requests-optimizer.service.spec.ts index cd590d4d318..48672c3edc7 100644 --- a/projects/core/src/occ/services/occ-requests-optimizer.service.spec.ts +++ b/projects/core/src/occ/services/occ-requests-optimizer.service.spec.ts @@ -1,8 +1,8 @@ +import { HttpClientTestingModule } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; -import { OccRequestsOptimizerService } from './occ-requests-optimizer.service'; +import { lastValueFrom, of } from 'rxjs'; import { ScopedDataWithUrl } from './occ-fields.service'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { of } from 'rxjs'; +import { OccRequestsOptimizerService } from './occ-requests-optimizer.service'; describe('OccRequestsOptimizerService', () => { let service: OccRequestsOptimizerService; @@ -34,10 +34,13 @@ describe('OccRequestsOptimizerService', () => { const result = service.scopedDataLoad(models, dataFactory); expect(result[0].scope).toEqual(models[0].scopedData.scope); - expect(await result[0].data$.toPromise()).toEqual({ ala: '1' }); + expect(await lastValueFrom(result[0].data$)).toEqual({ ala: '1' }); expect(result[1].scope).toEqual(models[1].scopedData.scope); - expect(await result[1].data$.toPromise()).toEqual({ ma: '2', kota: '3' }); + expect(await lastValueFrom(result[1].data$)).toEqual({ + ma: '2', + kota: '3', + }); }); }); }); diff --git a/projects/core/src/product/facade/product.service.spec.ts b/projects/core/src/product/facade/product.service.spec.ts index 8d16454bcd2..78b29006b4c 100644 --- a/projects/core/src/product/facade/product.service.spec.ts +++ b/projects/core/src/product/facade/product.service.spec.ts @@ -2,7 +2,7 @@ import { inject, TestBed } from '@angular/core/testing'; import * as ngrxStore from '@ngrx/store'; import { Store, StoreModule } from '@ngrx/store'; import { DEFAULT_SCOPE, ProductLoadingService } from '@spartacus/core'; -import { of } from 'rxjs'; +import { lastValueFrom, of } from 'rxjs'; import { Product } from '../../model/product.model'; import { PRODUCT_FEATURE, StateWithProduct } from '../store/product-state'; import * as fromStoreReducers from '../store/reducers/index'; @@ -54,24 +54,26 @@ describe('ProductService', () => { describe('get(productCode)', () => { it('should be able to get product by code', async () => { - const result: Product = await service.get('testId').toPromise(); + const result: Product = await lastValueFrom(service.get('testId')); expect(result).toEqual(mockProduct('testId')); }); it('should be able to get product by code and scope', async () => { - const result: Product = await service.get('testId', 'scope').toPromise(); + const result: Product = await lastValueFrom( + service.get('testId', 'scope') + ); expect(result).toEqual(mockProduct('testId', ['scope'])); }); it('should be able to get product by code and scopes', async () => { - const result: Product = await service - .get('testId', ['scope1', 'scope2']) - .toPromise(); + const result: Product = await lastValueFrom( + service.get('testId', ['scope1', 'scope2']) + ); expect(result).toEqual(mockProduct('testId', ['scope1', 'scope2'])); }); it('should return undefined when no product code was provided', async () => { - const result: Product = await service.get(undefined).toPromise(); + const result: Product = await lastValueFrom(service.get(undefined)); expect(result).toEqual(undefined); }); }); @@ -129,7 +131,9 @@ describe('ProductService', () => { spyOnProperty(ngrxStore, 'select').and.returnValue( () => () => of({ value: mockedProduct }) ); - const result: Product = await service.get('existingProduct').toPromise(); + const result: Product = await lastValueFrom( + service.get('existingProduct') + ); expect(result).toBeTruthy(); }); }); diff --git a/projects/core/src/product/services/product-loading.service.spec.ts b/projects/core/src/product/services/product-loading.service.spec.ts index b50d76790d9..ade468fbc01 100644 --- a/projects/core/src/product/services/product-loading.service.spec.ts +++ b/projects/core/src/product/services/product-loading.service.spec.ts @@ -4,7 +4,16 @@ import { Actions } from '@ngrx/effects'; import * as ngrxStore from '@ngrx/store'; import { Action, Store, StoreModule } from '@ngrx/store'; import { cold, getTestScheduler, hot } from 'jasmine-marbles'; -import { EMPTY, NEVER, Observable, of, Subject, timer } from 'rxjs'; +import { + EMPTY, + firstValueFrom, + lastValueFrom, + NEVER, + Observable, + of, + Subject, + timer, +} from 'rxjs'; import { delay, switchMap, take } from 'rxjs/operators'; import { CxEvent } from '../../event/cx-event'; import { EventService } from '../../event/event.service'; @@ -81,7 +90,7 @@ describe('ProductLoadingService', () => { spyOnProperty(ngrxStore, 'select').and.returnValue( () => () => of(mockProduct) ); - const result: Product = await service.get(code, ['']).toPromise(); + const result: Product = await lastValueFrom(service.get(code, [''])); expect(result).toEqual(mockProduct); }); @@ -97,10 +106,9 @@ describe('ProductLoadingService', () => { ) ); - const result: Product = await service - .get(code, ['scope1', 'scope2']) - .pipe(take(1)) - .toPromise(); + const result: Product = await firstValueFrom( + service.get(code, ['scope1', 'scope2']) + ); expect(result).toEqual({ code, name: 'test' }); }); @@ -113,10 +121,9 @@ describe('ProductLoadingService', () => { ) ); - const result: Product = await service - .get(code, ['scope1', 'scope2']) - .pipe(take(1)) - .toPromise(); + const result: Product = await firstValueFrom( + service.get(code, ['scope1', 'scope2']) + ); expect(result).toEqual(undefined); }); @@ -134,10 +141,9 @@ describe('ProductLoadingService', () => { ) ); - const result: Product = await service - .get(code, ['scope1', 'scope2']) - .pipe(take(1)) - .toPromise(); + const result: Product = await firstValueFrom( + service.get(code, ['scope1', 'scope2']) + ); expect(result).toEqual({ code, name: 'second', @@ -219,13 +225,11 @@ describe('ProductLoadingService', () => { it('should be able to trigger the product load action for a product.', async () => { spyOn(store, 'dispatch').and.stub(); - await service - .get('productCode', ['']) - .pipe( - delay(0), // give actions some time for dispatch - take(1) + await firstValueFrom( + service.get('productCode', ['']).pipe( + delay(0) // give actions some time for dispatch ) - .toPromise(); + ); expect(store.dispatch).toHaveBeenCalledWith( new ProductActions.LoadProduct('productCode') @@ -236,10 +240,7 @@ describe('ProductLoadingService', () => { spyOn(store, 'dispatch').and.stub(); service.get('productCode', ['']).pipe(take(1)).subscribe(); - await service - .get('productCode', ['']) - .pipe(delay(0), take(1)) - .toPromise(); + await firstValueFrom(service.get('productCode', ['']).pipe(delay(0))); expect(store.dispatch).toHaveBeenCalledTimes(1); }); diff --git a/projects/core/src/routing/configurable-routes/secure-portal-config/secure-portal-config-initializer.ts b/projects/core/src/routing/configurable-routes/secure-portal-config/secure-portal-config-initializer.ts index 4be025c7262..e7d60998110 100644 --- a/projects/core/src/routing/configurable-routes/secure-portal-config/secure-portal-config-initializer.ts +++ b/projects/core/src/routing/configurable-routes/secure-portal-config/secure-portal-config-initializer.ts @@ -5,7 +5,7 @@ */ import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; +import { Observable, lastValueFrom } from 'rxjs'; import { map, switchMap, take, tap } from 'rxjs/operators'; import { ConfigInitializer } from '../../../config/config-initializer/config-initializer'; import { ConfigInitializerService } from '../../../config/config-initializer/config-initializer.service'; @@ -17,7 +17,7 @@ import { RoutingConfig } from '../config/routing-config'; @Injectable({ providedIn: 'root' }) export class SecurePortalConfigInitializer implements ConfigInitializer { readonly scopes = ['routing']; - readonly configFactory = () => this.resolveConfig().toPromise(); + readonly configFactory = () => lastValueFrom(this.resolveConfig()); constructor( protected baseSiteService: BaseSiteService, diff --git a/projects/core/src/routing/services/activated-routes.service.spec.ts b/projects/core/src/routing/services/activated-routes.service.spec.ts index 0beb0b77ec6..18e3be2e0b3 100644 --- a/projects/core/src/routing/services/activated-routes.service.spec.ts +++ b/projects/core/src/routing/services/activated-routes.service.spec.ts @@ -7,8 +7,7 @@ import { Router, RouterEvent, } from '@angular/router'; -import { Subject } from 'rxjs'; -import { take } from 'rxjs/operators'; +import { firstValueFrom, Subject } from 'rxjs'; import { ActivatedRoutesService } from './activated-routes.service'; describe('ActivatedRoutesService', () => { @@ -40,7 +39,7 @@ describe('ActivatedRoutesService', () => { describe(`routes$`, () => { it('should emit on subscription', async () => { - expect(await service.routes$.pipe(take(1)).toPromise()).toEqual([ + expect(await firstValueFrom(service.routes$)).toEqual([ router.routerState.snapshot.root, ]); }); @@ -80,7 +79,7 @@ describe('ActivatedRoutesService', () => { }; (router as any).routerState = mockRouterState; // as any => mitigate readonly - expect(await service.routes$.pipe(take(1)).toPromise()).toEqual([ + expect(await firstValueFrom(service.routes$)).toEqual([ mockRouterState.snapshot.root, mockRouterState.snapshot.root.firstChild, mockRouterState.snapshot.root.firstChild.firstChild, diff --git a/projects/core/src/site-context/config/config-loader/site-context-config-initializer.ts b/projects/core/src/site-context/config/config-loader/site-context-config-initializer.ts index 8e459880d77..a118a9b2c89 100644 --- a/projects/core/src/site-context/config/config-loader/site-context-config-initializer.ts +++ b/projects/core/src/site-context/config/config-loader/site-context-config-initializer.ts @@ -5,7 +5,7 @@ */ import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; +import { Observable, lastValueFrom } from 'rxjs'; import { filter, map, take } from 'rxjs/operators'; import { ConfigInitializer } from '../../../config/config-initializer/config-initializer'; import { BaseSite } from '../../../model/misc.model'; @@ -23,7 +23,7 @@ import { SiteContextConfig } from '../site-context-config'; @Injectable({ providedIn: 'root' }) export class SiteContextConfigInitializer implements ConfigInitializer { readonly scopes = ['context']; - readonly configFactory = () => this.resolveConfig().toPromise(); + readonly configFactory = () => lastValueFrom(this.resolveConfig()); constructor( protected baseSiteService: BaseSiteService, diff --git a/projects/core/src/site-context/providers/context-service-providers.ts b/projects/core/src/site-context/providers/context-service-providers.ts index 355aac9e09d..7124bfea15f 100644 --- a/projects/core/src/site-context/providers/context-service-providers.ts +++ b/projects/core/src/site-context/providers/context-service-providers.ts @@ -5,6 +5,7 @@ */ import { APP_INITIALIZER, Provider } from '@angular/core'; +import { lastValueFrom } from 'rxjs'; import { tap } from 'rxjs/operators'; import { ConfigInitializerService } from '../../config/config-initializer/config-initializer.service'; import { Config } from '../../config/config-tokens'; @@ -18,9 +19,8 @@ export function initializeContext( siteContextRoutesHandler: SiteContextRoutesHandler ): () => Promise { return () => { - return configInit - .getStable('context') - .pipe( + return lastValueFrom( + configInit.getStable('context').pipe( tap(() => { // `siteContextRoutesHandler.init()` should be executed before CurrencyInitializer, // LanguageInitializer and BaseSiteInitializer @@ -30,7 +30,7 @@ export function initializeContext( siteContextRoutesHandler.init(); }) ) - .toPromise(); + ); }; } diff --git a/projects/core/src/site-context/site-context.module.ts b/projects/core/src/site-context/site-context.module.ts index 4877a54f6b5..4f55ef898bb 100644 --- a/projects/core/src/site-context/site-context.module.ts +++ b/projects/core/src/site-context/site-context.module.ts @@ -6,8 +6,8 @@ import { ModuleWithProviders, NgModule } from '@angular/core'; import { - ConfigInitializer, CONFIG_INITIALIZER, + ConfigInitializer, } from '../config/config-initializer/config-initializer'; import { provideDefaultConfigFactory } from '../config/config-providers'; import { provideConfigValidator } from '../config/config-validator/config-validator'; @@ -34,7 +34,7 @@ export function initSiteContextConfig( /** * Load config for `context` from backend only when there is no static config for `context.baseSite` */ - if (!config.context || !config.context[BASE_SITE_CONTEXT_ID]) { + if (!config.context?.[BASE_SITE_CONTEXT_ID]) { return configInitializer; } return null; diff --git a/projects/core/src/user/store/effects/notification-preference.effect.spec.ts b/projects/core/src/user/store/effects/notification-preference.effect.spec.ts index 61971919d8f..cdb60dbfcb7 100644 --- a/projects/core/src/user/store/effects/notification-preference.effect.spec.ts +++ b/projects/core/src/user/store/effects/notification-preference.effect.spec.ts @@ -3,11 +3,11 @@ import { provideMockActions } from '@ngrx/effects/testing'; import { Action } from '@ngrx/store'; import { cold, hot } from 'jasmine-marbles'; import { Observable, of, throwError } from 'rxjs'; -import { UserNotificationPreferenceConnector } from '../../connectors/notification-preference/user-notification-preference.connector'; +import { NotificationPreference } from '../../../model/notification-preference.model'; import { UserNotificationPreferenceAdapter } from '../../connectors/notification-preference/user-notification-preference.adapter'; +import { UserNotificationPreferenceConnector } from '../../connectors/notification-preference/user-notification-preference.connector'; import { UserActions } from '../actions/index'; import * as fromEffect from './notification-preference.effect'; -import { NotificationPreference } from '../../../model/notification-preference.model'; const userId = 'testUser'; const mockNotificationPreference: NotificationPreference[] = [ @@ -63,7 +63,7 @@ describe('Notification Preference Effect', () => { it('should return LoadNotificationPreferencesFail action', () => { spyOn(userNotificationPreferenceConnector, 'loadAll').and.returnValue( - throwError(error) + throwError(() => error) ); const action = new UserActions.LoadNotificationPreferences(userId); @@ -104,7 +104,7 @@ describe('Notification Preference Effect', () => { it('should return NotificationPreferencesFail action', () => { spyOn(userNotificationPreferenceConnector, 'update').and.returnValue( - throwError(error) + throwError(() => error) ); const action = new UserActions.UpdateNotificationPreferences({ diff --git a/projects/core/src/user/store/effects/product-interests.effect.spec.ts b/projects/core/src/user/store/effects/product-interests.effect.spec.ts index 0e78025aace..47f211dbdcc 100644 --- a/projects/core/src/user/store/effects/product-interests.effect.spec.ts +++ b/projects/core/src/user/store/effects/product-interests.effect.spec.ts @@ -1,17 +1,17 @@ -import { TestBed } from '@angular/core/testing'; import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { provideMockActions } from '@ngrx/effects/testing'; -import * as fromInterestsEffect from './product-interests.effect'; -import { UserActions } from '../actions/index'; +import { TestBed } from '@angular/core/testing'; import { Actions } from '@ngrx/effects'; +import { provideMockActions } from '@ngrx/effects/testing'; +import { cold, hot } from 'jasmine-marbles'; import { of, throwError } from 'rxjs'; import { NotificationType, ProductInterestSearchResult, } from '../../../model/product-interest.model'; -import { cold, hot } from 'jasmine-marbles'; -import { UserInterestsConnector } from '../../connectors/interests/user-interests.connector'; import { UserInterestsAdapter } from '../../connectors/interests/user-interests.adapter'; +import { UserInterestsConnector } from '../../connectors/interests/user-interests.connector'; +import { UserActions } from '../actions/index'; +import * as fromInterestsEffect from './product-interests.effect'; const loadParams = { userId: 'qingyu@sap.com', @@ -63,7 +63,7 @@ describe('Product Interests Effect', () => { }); it('should be able to handle failures for load product interests', () => { spyOn(userInterestConnector, 'getInterests').and.returnValue( - throwError('Error') + throwError(() => 'Error') ); const action = new UserActions.LoadProductInterests(loadParams); const completion = new UserActions.LoadProductInterestsFail(undefined); @@ -141,7 +141,7 @@ describe('Product Interests Effect', () => { it('should be able to handle failures for remove product interest', () => { spyOn(userInterestConnector, 'removeInterest').and.returnValue( - throwError('Error') + throwError(() => 'Error') ); const action = new UserActions.RemoveProductInterest(delParams); const completion = new UserActions.RemoveProductInterestFail(undefined); diff --git a/projects/core/src/user/store/effects/user-consents.effect.spec.ts b/projects/core/src/user/store/effects/user-consents.effect.spec.ts index 9e3323923b8..c06312f4fba 100644 --- a/projects/core/src/user/store/effects/user-consents.effect.spec.ts +++ b/projects/core/src/user/store/effects/user-consents.effect.spec.ts @@ -102,7 +102,7 @@ describe('User Consents effect', () => { msg: 'Mock error', }; spyOn(userConsentAdapter, 'giveConsent').and.returnValue( - throwError(mockError) + throwError(() => mockError) ); const action = new UserActions.TransferAnonymousConsent({ @@ -129,7 +129,7 @@ describe('User Consents effect', () => { msg: 'Mock error', }; spyOn(userConsentAdapter, 'giveConsent').and.returnValue( - throwError(mockError) + throwError(() => mockError) ); const action = new UserActions.GiveUserConsent({ diff --git a/projects/core/src/util/command-query/command.service.spec.ts b/projects/core/src/util/command-query/command.service.spec.ts index 2fa77694163..a8c73f9e7bf 100644 --- a/projects/core/src/util/command-query/command.service.spec.ts +++ b/projects/core/src/util/command-query/command.service.spec.ts @@ -6,6 +6,7 @@ import { Observable, PartialObserver, Subject, + config, of, throwError, } from 'rxjs'; @@ -34,6 +35,22 @@ describe('CommandService', () => { let request1: Subject; let request2: Subject; + // TODO: CXSPA-4870 verify if can be avoided + let originalOnUnhandledError: ((err: any) => void) | null; + + beforeAll(() => { + // configure rxjs to not crash node instance with thrown errors + // TODO: CXSPA-4870 verify if can be avoided + originalOnUnhandledError = config.onUnhandledError; + config.onUnhandledError = () => {}; + }); + + afterAll(() => { + // reset rxjs configuration + // TODO: CXSPA-4870 verify if can be avoided + config.onUnhandledError = originalOnUnhandledError; + }); + beforeEach(() => { TestBed.configureTestingModule({}); service = TestBed.inject(CommandService); @@ -273,9 +290,11 @@ describe('CommandService', () => { it('should cancel in-progress requests', () => { const observer1 = createObserverSpy('observer1'); - cancelPrevCommand.execute(request1).subscribe(observer1); + const sub1 = cancelPrevCommand.execute(request1); + // cancelPrevCommand.execute(request1).subscribe(observer1); cancelPrevCommand.execute(request2).subscribe(); + sub1.subscribe(observer1); request1.next('next'); expect(observer1.next).not.toHaveBeenCalled(); diff --git a/projects/core/src/util/command-query/command.service.ts b/projects/core/src/util/command-query/command.service.ts index a0b473d9d0f..471d89a2d18 100644 --- a/projects/core/src/util/command-query/command.service.ts +++ b/projects/core/src/util/command-query/command.service.ts @@ -15,6 +15,7 @@ import { zip, } from 'rxjs'; import { + TapObserver, catchError, concatMap, finalize, @@ -53,6 +54,19 @@ export class CommandService implements OnDestroy { const commands$ = new Subject(); const results$ = new Subject>(); + // We have to specify selectively the handlers after RxJS version 7.3.0. + // Otherwise, the `unsubscribe` handler would be forwarded implicitly + // to `notifier$` when calling `tap(notifier$)`. + // But we don't want to forward `unsubscribe`, in particular. + // To see more details, please check: https://github.com/ReactiveX/rxjs/pull/6527 + const notify = ( + notifier$: ReplaySubject + ): Partial> => ({ + next: (x) => notifier$.next(x), + error: (e) => notifier$.error(e), + complete: () => notifier$.complete(), + }); + let process$: Observable; switch (options?.strategy) { @@ -61,7 +75,7 @@ export class CommandService implements OnDestroy { process$ = zip(commands$, results$).pipe( switchMap(([cmd, notifier$]) => defer(() => commandFactory(cmd)).pipe( - tap(notifier$), + tap(notify(notifier$)), catchError(() => EMPTY), finalize(() => { // do not overwrite existing existing ending state @@ -83,7 +97,7 @@ export class CommandService implements OnDestroy { process$ = zip(commands$, results$).pipe( mergeMap(([cmd, notifier$]) => defer(() => commandFactory(cmd)).pipe( - tap(notifier$), + tap(notify(notifier$)), catchError(() => EMPTY) ) ) @@ -95,7 +109,7 @@ export class CommandService implements OnDestroy { process$ = zip(commands$, results$).pipe( concatMap(([cmd, notifier$]) => defer(() => commandFactory(cmd)).pipe( - tap(notifier$), + tap(notify(notifier$)), catchError(() => EMPTY) ) ) diff --git a/projects/core/src/util/rxjs/back-off.spec.ts b/projects/core/src/util/rxjs/back-off.spec.ts index 25c8a1f2e9a..cd932d9686f 100644 --- a/projects/core/src/util/rxjs/back-off.spec.ts +++ b/projects/core/src/util/rxjs/back-off.spec.ts @@ -20,7 +20,7 @@ describe(`backOff`, () => { if (calledTimes === 3) { return of(recoveredValue); } - return throwError(error); + return throwError(() => error); }); const test$ = source$.pipe(backOff()); @@ -37,7 +37,7 @@ describe(`backOff`, () => { it(`should NOT be able to recover`, fakeAsync(() => { const initialError = 'error'; - const source$ = throwError(initialError); + const source$ = throwError(() => initialError); const test$ = source$.pipe(backOff()); let result: string | undefined; @@ -69,10 +69,10 @@ describe(`backOff`, () => { }); describe(`when the source throws an error`, () => { - describe(`errFn`, () => { + describe(`shouldRetry function`, () => { describe(`evaluates to false`, () => { it(`should not retry and just re-throw the error`, (done) => { - const source$ = throwError('error'); + const source$ = throwError(() => 'error'); const test$ = source$.pipe(backOff({ shouldRetry: () => false })); test$.subscribe({ @@ -89,7 +89,7 @@ describe(`backOff`, () => { it(`should re-throw the initial error`, fakeAsync(() => { const initialError = 'error'; - const source$ = throwError(initialError); + const source$ = throwError(() => initialError); const test$ = source$.pipe(backOff({ shouldRetry: doBackOff })); let result: string | undefined; @@ -110,7 +110,7 @@ describe(`backOff`, () => { const initialError = 'error'; const recoveredValue = 'xxx'; - const error$ = throwError(initialError); + const error$ = throwError(() => initialError); // at first, we throw an error by returning the false const recovery$ = new BehaviorSubject(false); const source$ = recovery$.pipe( @@ -153,7 +153,9 @@ describe(`backOff`, () => { }; const error$ = new BehaviorSubject(initialError); - const source$ = error$.pipe(switchMap((error) => throwError(error))); + const source$ = error$.pipe( + switchMap((error) => throwError(() => error)) + ); let errorResult: HttpErrorModel | undefined; let result: HttpErrorModel | undefined; @@ -187,7 +189,7 @@ describe(`backOff`, () => { it(`should use the provided maxTries option`, fakeAsync(() => { const initialError = 'error'; - const source$ = throwError(initialError); + const source$ = throwError(() => initialError); const test$ = source$.pipe( backOff({ shouldRetry: doBackOff, maxTries: 2 }) ); @@ -207,7 +209,7 @@ describe(`backOff`, () => { it(`should use the provided delay option`, fakeAsync(() => { const initialError = 'error'; - const source$ = throwError(initialError); + const source$ = throwError(() => initialError); const test$ = source$.pipe( backOff({ shouldRetry: doBackOff, delay: 100 }) ); @@ -227,7 +229,7 @@ describe(`backOff`, () => { it(`should use both the provided maxTries and delay options`, fakeAsync(() => { const initialError = 'error'; - const source$ = throwError(initialError); + const source$ = throwError(() => initialError); const test$ = source$.pipe( backOff({ shouldRetry: doBackOff, maxTries: 2, delay: 100 }) ); diff --git a/projects/core/src/util/rxjs/back-off.ts b/projects/core/src/util/rxjs/back-off.ts index c3c4302ae4a..7a443b24e0f 100644 --- a/projects/core/src/util/rxjs/back-off.ts +++ b/projects/core/src/util/rxjs/back-off.ts @@ -4,16 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { - Observable, - of, - OperatorFunction, - range, - throwError, - timer, - zip, -} from 'rxjs'; -import { map, mergeMap, retryWhen } from 'rxjs/operators'; +import { OperatorFunction, timer } from 'rxjs'; +import { retry } from 'rxjs/operators'; import { HttpErrorModel } from '../../model/misc.model'; /** @@ -41,7 +33,6 @@ export interface BackOffOptions { * * Source: https://angular.io/guide/practical-observable-usage#exponential-backoff * - * @param errFn for which to perform exponential back-off * @param options such as defining `maxTries`, or `delay` * @returns either the original error (if the given `errFn` return `false`), or the */ @@ -50,28 +41,22 @@ export function backOff(options?: BackOffOptions): OperatorFunction { const maxTries = options?.maxTries ?? 3; const delay = options?.delay ?? 300; - // creates a range of maximum retries - starting from 1, up until the given `maxTries` - const retry$ = range(1, maxTries + 1); return (source$) => source$.pipe( // retries the source stream in case of an error. - retryWhen((attempts$: Observable) => - // emits only when both emit at the same time. In practice, this means: emit when error happens again and retried - zip(attempts$, retry$).pipe( - mergeMap(([attemptError, currentRetry]) => { - // if we've re-tried more than the maxTries, OR - // if the source error is not the one we want to exponentially retry - if (currentRetry > maxTries || !shouldRetry(attemptError)) { - return throwError(attemptError); - } + retry({ + delay: (attemptError: HttpErrorModel | Error, currentRetry) => { + // if we've re-tried more than the maxTries, OR + // if the source error is not the one we want to exponentially retry + if (currentRetry > maxTries || !shouldRetry(attemptError)) { + throw attemptError; + } - return of(currentRetry); - }), // exponential - map((currentRetry) => currentRetry * currentRetry), + const exponent = currentRetry * currentRetry; // back-off - mergeMap((exponent) => timer(exponent * delay)) - ) - ) + return timer(exponent * delay); + }, + }) ); } diff --git a/projects/schematics/src/dependencies.json b/projects/schematics/src/dependencies.json index 9adc7a210cb..7913bff0736 100644 --- a/projects/schematics/src/dependencies.json +++ b/projects/schematics/src/dependencies.json @@ -12,7 +12,7 @@ "i18next": "^21.10.0", "i18next-http-backend": "^1.4.5", "i18next-resources-to-backend": "^1.1.2", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/schematics": { "@angular-devkit/core": "^15.2.9", @@ -37,7 +37,7 @@ "@ngrx/store": "^15.3.0", "@spartacus/core": "6.6.0-1", "ngx-infinite-scroll": "^15.0.0", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/styles": { "@fontsource/open-sans": "^4.5.14", @@ -69,7 +69,7 @@ "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", "@spartacus/user": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/cart": { "@angular-devkit/schematics": "^15.2.9", @@ -86,7 +86,7 @@ "@spartacus/styles": "6.6.0-1", "@spartacus/user": "6.6.0-1", "bootstrap": "^4.6.2", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/checkout": { "@angular-devkit/schematics": "^15.2.9", @@ -104,7 +104,7 @@ "@spartacus/styles": "6.6.0-1", "@spartacus/user": "6.6.0-1", "bootstrap": "^4.6.2", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/customer-ticketing": { "@angular-devkit/schematics": "^15.2.9", @@ -117,7 +117,7 @@ "@spartacus/schematics": "6.6.0-1", "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/order": { "@angular-devkit/schematics": "^15.2.9", @@ -135,7 +135,7 @@ "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", "@spartacus/user": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/organization": { "@angular-devkit/schematics": "^15.2.9", @@ -154,7 +154,7 @@ "@spartacus/styles": "6.6.0-1", "@spartacus/user": "6.6.0-1", "bootstrap": "^4.6.2", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/pdf-invoices": { "@angular-devkit/schematics": "^15.2.9", @@ -165,7 +165,7 @@ "@spartacus/schematics": "6.6.0-1", "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/pickup-in-store": { "@angular-devkit/schematics": "^15.2.9", @@ -184,7 +184,7 @@ "@spartacus/styles": "6.6.0-1", "@spartacus/user": "6.6.0-1", "bootstrap": "^4.6.2", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/product": { "@angular-devkit/schematics": "^15.2.9", @@ -196,7 +196,7 @@ "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", "bootstrap": "^4.6.2", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/product-configurator": { "@angular-devkit/schematics": "^15.2.9", @@ -214,7 +214,7 @@ "@spartacus/schematics": "6.6.0-1", "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/qualtrics": { "@angular-devkit/schematics": "^15.2.9", @@ -224,7 +224,7 @@ "@spartacus/schematics": "6.6.0-1", "@spartacus/styles": "6.6.0-1", "bootstrap": "^4.6.2", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/requested-delivery-date": { "@angular-devkit/schematics": "^15.2.9", @@ -238,7 +238,7 @@ "@spartacus/schematics": "6.6.0-1", "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/smartedit": { "@angular-devkit/schematics": "^15.2.9", @@ -246,7 +246,7 @@ "@angular/core": "^15.2.9", "@spartacus/core": "6.6.0-1", "@spartacus/schematics": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/storefinder": { "@angular-devkit/schematics": "^15.2.9", @@ -261,7 +261,7 @@ "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", "bootstrap": "^4.6.2", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/tracking": { "@angular-devkit/schematics": "^15.2.9", @@ -269,7 +269,7 @@ "@angular/core": "^15.2.9", "@spartacus/core": "6.6.0-1", "@spartacus/schematics": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/user": { "@angular-devkit/schematics": "^15.2.9", @@ -283,7 +283,7 @@ "@spartacus/schematics": "6.6.0-1", "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/cdc": { "@angular-devkit/schematics": "^15.2.9", @@ -300,7 +300,7 @@ "@spartacus/schematics": "6.6.0-1", "@spartacus/storefront": "6.6.0-1", "@spartacus/user": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/cds": { "@angular-devkit/schematics": "^15.2.9", @@ -314,7 +314,7 @@ "@spartacus/schematics": "6.6.0-1", "@spartacus/storefront": "6.6.0-1", "@spartacus/tracking": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/digital-payments": { "@angular-devkit/schematics": "^15.2.9", @@ -328,7 +328,7 @@ "@spartacus/core": "6.6.0-1", "@spartacus/schematics": "6.6.0-1", "@spartacus/storefront": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/epd-visualization": { "@angular-devkit/schematics": "^15.2.9", @@ -343,7 +343,7 @@ "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", "bootstrap": "^4.6.2", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/s4om": { "@angular-devkit/schematics": "^15.2.9", @@ -356,7 +356,7 @@ "@spartacus/schematics": "6.6.0-1", "@spartacus/storefront": "6.6.0-1", "@spartacus/styles": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "@spartacus/segment-refs": { "@angular-devkit/schematics": "^15.2.9", @@ -364,7 +364,7 @@ "@angular/core": "^15.2.9", "@spartacus/core": "6.6.0-1", "@spartacus/schematics": "6.6.0-1", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "storefrontapp": { "@angular/animations": "^15.2.9", @@ -395,7 +395,7 @@ "i18next-http-backend": "^1.4.5", "i18next-resources-to-backend": "^1.1.2", "ngx-infinite-scroll": "^15.0.0", - "rxjs": "^6.6.0", + "rxjs": "^7.8.0", "ts-loader": "^9.4.4", "tslib": "^2.6.2", "zone.js": "~0.12.0" diff --git a/projects/schematics/src/ng-add/__snapshots__/index_spec.ts.snap b/projects/schematics/src/ng-add/__snapshots__/index_spec.ts.snap index 798e06bfd45..cb6e25c523b 100644 --- a/projects/schematics/src/ng-add/__snapshots__/index_spec.ts.snap +++ b/projects/schematics/src/ng-add/__snapshots__/index_spec.ts.snap @@ -75,7 +75,7 @@ exports[`Spartacus Schematics: ng-add should add spartacus properly with SSR 2`] "i18next-http-backend": "^1.4.5", "i18next-resources-to-backend": "^1.1.2", "ngx-infinite-scroll": "^15.0.0", - "rxjs": "^6.6.0", + "rxjs": "~7.8.0", "ts-loader": "^9.4.4", "tslib": "^2.3.0", "zone.js": "~0.12.0" diff --git a/projects/storefrontlib/cms-structure/page/component/handlers/lazy-component.handler.spec.ts b/projects/storefrontlib/cms-structure/page/component/handlers/lazy-component.handler.spec.ts index 435569f3dd0..ae9da7993ef 100644 --- a/projects/storefrontlib/cms-structure/page/component/handlers/lazy-component.handler.spec.ts +++ b/projects/storefrontlib/cms-structure/page/component/handlers/lazy-component.handler.spec.ts @@ -1,9 +1,9 @@ import { TestBed } from '@angular/core/testing'; -import { LazyComponentHandler } from './lazy-component.handler'; import { Priority } from '@spartacus/core'; import { DefaultComponentHandler } from '@spartacus/storefront'; -import { of } from 'rxjs'; +import { lastValueFrom, of } from 'rxjs'; +import { LazyComponentHandler } from './lazy-component.handler'; import createSpy = jasmine.createSpy; class MockDefaultComponentHandler { @@ -50,9 +50,9 @@ describe('LazyComponentHandler', () => { it('should resolve component and pass it to standard launcher', async () => { const resolvedComponent = 'testData'; const mapping = () => new Promise((res) => res(resolvedComponent)); - await service - .launcher({ component: mapping }, undefined, undefined) - .toPromise(); + await lastValueFrom( + service.launcher({ component: mapping }, undefined, undefined) + ); const defaultHandler = TestBed.inject(DefaultComponentHandler); diff --git a/projects/storefrontlib/cms-structure/page/component/services/component-data.provider.spec.ts b/projects/storefrontlib/cms-structure/page/component/services/component-data.provider.spec.ts index dc5cf6c6f05..8c0e69022f6 100644 --- a/projects/storefrontlib/cms-structure/page/component/services/component-data.provider.spec.ts +++ b/projects/storefrontlib/cms-structure/page/component/services/component-data.provider.spec.ts @@ -6,7 +6,7 @@ import { ComponentDataProvider } from './component-data.provider'; class MockCmsComponentsService { getStaticData() { - return of({}); + return {}; } } @@ -44,7 +44,7 @@ describe('ComponentDataProvider', () => { }); it('should load data from cms service', () => { - spyOn(cmsService, 'getComponentData'); + spyOn(cmsService, 'getComponentData').and.callThrough(); service.get('123').subscribe().unsubscribe(); expect(cmsService.getComponentData).toHaveBeenCalledWith('123'); }); diff --git a/projects/storefrontlib/cms-structure/services/cms-features.service.spec.ts b/projects/storefrontlib/cms-structure/services/cms-features.service.spec.ts index 415565fcc52..84bffb241fb 100644 --- a/projects/storefrontlib/cms-structure/services/cms-features.service.spec.ts +++ b/projects/storefrontlib/cms-structure/services/cms-features.service.spec.ts @@ -6,6 +6,7 @@ import { provideConfig, provideDefaultConfig, } from '@spartacus/core'; +import { lastValueFrom } from 'rxjs'; import { CmsFeaturesService } from './cms-features.service'; const mockCmsConfig: CmsConfig = { @@ -108,7 +109,7 @@ describe('CmsFeaturesService', () => { it('should return feature module', async () => { // initialize feature - await service.getCmsMapping('component1').toPromise(); + await lastValueFrom(service.getCmsMapping('component1')); const module = service.getModule('component1'); @@ -119,7 +120,7 @@ describe('CmsFeaturesService', () => { it('should return dependency injectors', async () => { // initialize feature - await service.getCmsMapping('component2').toPromise(); + await lastValueFrom(service.getCmsMapping('component2')); const module = service.getModule('component2'); expect(module).toBeTruthy(); diff --git a/projects/storefrontlib/layout/direction/direction.service.ts b/projects/storefrontlib/layout/direction/direction.service.ts index 5c55393e07e..6abcf45d440 100644 --- a/projects/storefrontlib/layout/direction/direction.service.ts +++ b/projects/storefrontlib/layout/direction/direction.service.ts @@ -10,7 +10,7 @@ import { LanguageService, WindowRef, } from '@spartacus/core'; -import { Subscription } from 'rxjs'; +import { lastValueFrom, Subscription } from 'rxjs'; import { tap } from 'rxjs/operators'; import { DirectionConfig } from './config/direction.config'; import { Direction, DirectionMode } from './config/direction.model'; @@ -44,9 +44,8 @@ export class DirectionService implements OnDestroy { * Initializes the layout direction for the storefront. */ initialize(): Promise { - return this.configInit - .getStable('direction') - .pipe( + return lastValueFrom( + this.configInit.getStable('direction').pipe( tap((config: DirectionConfig) => { this.config = config?.direction; if (this.config?.detect) { @@ -59,7 +58,7 @@ export class DirectionService implements OnDestroy { } }) ) - .toPromise(); + ); } /** diff --git a/projects/storefrontlib/package.json b/projects/storefrontlib/package.json index da42a8e2c7b..7f1d1d52789 100644 --- a/projects/storefrontlib/package.json +++ b/projects/storefrontlib/package.json @@ -25,7 +25,7 @@ "@ngrx/store": "^15.3.0", "@spartacus/core": "6.6.0-1", "ngx-infinite-scroll": "^15.0.0", - "rxjs": "^6.6.0" + "rxjs": "^7.8.0" }, "publishConfig": { "access": "public"