From e8eedb16d9c9bd37453748d07202dae040e75715 Mon Sep 17 00:00:00 2001 From: Ulrich Stellmacher Date: Tue, 22 Oct 2024 11:42:03 +0200 Subject: [PATCH] CXSPA-8270 fix random failing unit tests (#19417) --- ...figurator-storefront-utils.service.spec.ts | 123 +++++++++--------- .../quote-storefront-utils.service.spec.ts | 56 ++++---- 2 files changed, 89 insertions(+), 90 deletions(-) diff --git a/feature-libs/product-configurator/rulebased/components/service/configurator-storefront-utils.service.spec.ts b/feature-libs/product-configurator/rulebased/components/service/configurator-storefront-utils.service.spec.ts index 3f3609d4aff..71802a32209 100644 --- a/feature-libs/product-configurator/rulebased/components/service/configurator-storefront-utils.service.spec.ts +++ b/feature-libs/product-configurator/rulebased/components/service/configurator-storefront-utils.service.spec.ts @@ -20,6 +20,23 @@ import { Configurator } from '../../core/model/configurator.model'; import { ConfiguratorStorefrontUtilsService } from './configurator-storefront-utils.service'; import { ConfiguratorTestUtils } from '../../testing/configurator-test-utils'; +let mockedWindow: { + innerWidth?: number; + innerHeight?: number; + scrollY?: number; + scroll(): void; +} = { + innerWidth: 1000, + innerHeight: 1000, + scrollY: 1000, + scroll() {}, +}; +class MockedWindowRef extends WindowRef { + get nativeWindow(): Window | undefined { + return this.isBrowser() ? mockedWindow : undefined; + } +} + let isGroupVisited: Observable = of(false); const testSelector = 'test-configurator-overview-menu'; @@ -116,7 +133,6 @@ describe('ConfiguratorStorefrontUtilsService', () => { ); let windowRef: WindowRef; let keyboardFocusService: KeyboardFocusService; - let querySelectorOriginal: any; beforeEach(() => { mockRouterState.state.params.displayOnly = false; @@ -141,22 +157,26 @@ describe('ConfiguratorStorefrontUtilsService', () => { provide: ProductService, useClass: MockProductService, }, + { provide: WindowRef, useClass: MockedWindowRef }, ], schemas: [CUSTOM_ELEMENTS_SCHEMA], }); classUnderTest = TestBed.inject(ConfiguratorStorefrontUtilsService); fixture = TestBed.createComponent(MockComponent); htmlElem = fixture.nativeElement; - windowRef = TestBed.inject(WindowRef as Type); + windowRef = TestBed.inject(WindowRef); + mockedWindow.innerHeight = 1000; + mockedWindow.innerWidth = 1000; + mockedWindow.scrollY = 1000; keyboardFocusService = TestBed.inject( KeyboardFocusService as Type ); - querySelectorOriginal = document.querySelector; }); afterEach(() => { - document.querySelector = querySelectorOriginal; - document.body.removeChild(htmlElem); + if (htmlElem) { + document.body.removeChild(htmlElem); + } }); it('should be created', () => { @@ -174,9 +194,7 @@ describe('ConfiguratorStorefrontUtilsService', () => { it('should scroll to element', () => { const theElement = document.createElement('div'); - document.querySelector = jasmine - .createSpy('HTML Element') - .and.returnValue(theElement); + spyOn(windowRef.document, 'querySelector').and.returnValue(theElement); spyOn(theElement, 'getBoundingClientRect').and.returnValue( new DOMRect(100, 2000, 100, 100) ); @@ -308,9 +326,9 @@ describe('ConfiguratorStorefrontUtilsService', () => { it('should delegate to keyboard focus service', () => { spyOn(windowRef, 'isBrowser').and.returnValue(true); const focusedElements = createFocusedElements('ATTR', 2, 3); - document.querySelector = jasmine - .createSpy('HTML Element') - .and.returnValue(focusedElements); + spyOn(windowRef.document, 'querySelector').and.returnValue( + focusedElements[0] + ); spyOn(keyboardFocusService, 'findFocusable').and.returnValue( focusedElements ); @@ -320,9 +338,7 @@ describe('ConfiguratorStorefrontUtilsService', () => { it('should not delegate to keyboard focus service because form is undefined', () => { spyOn(windowRef, 'isBrowser').and.returnValue(true); - document.querySelector = jasmine - .createSpy('HTML Element') - .and.returnValue(undefined); + spyOn(windowRef.document, 'querySelector').and.returnValue(undefined); spyOn(keyboardFocusService, 'findFocusable').and.returnValue([]); classUnderTest.focusFirstActiveElement('elementSelector'); expect(keyboardFocusService.findFocusable).toHaveBeenCalledTimes(0); @@ -338,9 +354,9 @@ describe('ConfiguratorStorefrontUtilsService', () => { it('should not delegate to keyboard focus service because keyboard focus service returns no focusable elements', () => { spyOn(windowRef, 'isBrowser').and.returnValue(true); const focusedElements = createFocusedElements('ATTR', 2, 3); - document.querySelector = jasmine - .createSpy('HTML Element') - .and.returnValue(focusedElements); + spyOn(windowRef.document, 'querySelector').and.returnValue( + focusedElements[0] + ); spyOn(keyboardFocusService, 'findFocusable').and.returnValue([]); classUnderTest.focusFirstActiveElement('elementSelector'); expect(keyboardFocusService.findFocusable).toHaveBeenCalledTimes(1); @@ -394,9 +410,9 @@ describe('ConfiguratorStorefrontUtilsService', () => { .queryAll(By.css('label')) .map((el) => el.nativeNode); - document.querySelector = jasmine - .createSpy('HTML Element') - .and.returnValue(focusedElements); + spyOn(windowRef.document, 'querySelector').and.returnValue( + focusedElements + ); }); it('should not set focus because attribute does not contain any values', () => { @@ -458,9 +474,7 @@ describe('ConfiguratorStorefrontUtilsService', () => { spyOn(keyboardFocusService, 'findFocusable').and.returnValue( focusedElements ); - document.querySelector = jasmine - .createSpy('HTML Element') - .and.returnValue(undefined); + asSpy(windowRef.document.querySelector).and.returnValue(undefined); verify(focusedElements); }); @@ -526,9 +540,7 @@ describe('ConfiguratorStorefrontUtilsService', () => { it('should get HTML element based on query selector', () => { spyOn(windowRef, 'isBrowser').and.returnValue(true); const theElement = document.createElement('elementMock'); - document.querySelector = jasmine - .createSpy('HTML Element') - .and.returnValue(theElement); + spyOn(windowRef.document, 'querySelector').and.returnValue(theElement); expect(classUnderTest.getElement('elementMock')).toEqual(theElement); }); @@ -537,9 +549,7 @@ describe('ConfiguratorStorefrontUtilsService', () => { describe('changeStyling', () => { it('should change styling of HTML element', () => { const theElement = document.createElement('elementMock'); - document.querySelector = jasmine - .createSpy('HTML Element') - .and.returnValue(undefined); + spyOn(windowRef.document, 'querySelector').and.returnValue(undefined); classUnderTest.changeStyling('elementMock', 'position', 'sticky'); expect(theElement.style.position).not.toEqual('sticky'); @@ -547,9 +557,7 @@ describe('ConfiguratorStorefrontUtilsService', () => { it('should change styling of HTML element', () => { const theElement = document.createElement('elementMock'); - document.querySelector = jasmine - .createSpy('HTML Element') - .and.returnValue(theElement); + spyOn(windowRef.document, 'querySelector').and.returnValue(theElement); classUnderTest.changeStyling('elementMock', 'position', 'sticky'); expect(theElement.style.position).toEqual('sticky'); @@ -561,9 +569,7 @@ describe('ConfiguratorStorefrontUtilsService', () => { spyOn(windowRef, 'isBrowser').and.returnValue(true); const theElement = document.createElement('elementMock'); theElement.style.position = 'sticky'; - document.querySelector = jasmine - .createSpy('HTML Element') - .and.returnValue(undefined); + spyOn(windowRef.document, 'querySelector').and.returnValue(undefined); classUnderTest.removeStyling('elementMock', 'position'); expect(theElement.style.position).toEqual('sticky'); @@ -573,9 +579,7 @@ describe('ConfiguratorStorefrontUtilsService', () => { spyOn(windowRef, 'isBrowser').and.returnValue(true); const theElement = document.createElement('elementMock'); theElement.style.position = 'sticky'; - document.querySelector = jasmine - .createSpy('HTML Element') - .and.returnValue(theElement); + spyOn(windowRef.document, 'querySelector').and.returnValue(theElement); classUnderTest.removeStyling('elementMock', 'position'); expect(theElement.style.position).toBe(''); @@ -605,9 +609,7 @@ describe('ConfiguratorStorefrontUtilsService', () => { spyOn(windowRef, 'isBrowser').and.returnValue(true); const elements: Array = createElements('section', 10); - document.querySelectorAll = jasmine - .createSpy('section') - .and.returnValue(elements); + asSpy(windowRef.document.querySelectorAll).and.returnValue(elements); const htmlElements = classUnderTest.getElements('section'); @@ -627,7 +629,7 @@ describe('ConfiguratorStorefrontUtilsService', () => { it('should return number of pixels that the document is currently scrolled vertically', () => { spyOn(windowRef, 'isBrowser').and.returnValue(true); - spyOnProperty(window, 'scrollY').and.returnValue(250); + mockedWindow.scrollY = 250; const nativeWindow = windowRef.nativeWindow; if (nativeWindow) { expect(classUnderTest.getVerticallyScrolledPixels()).toBe(250); @@ -637,9 +639,7 @@ describe('ConfiguratorStorefrontUtilsService', () => { describe('hasScrollbar', () => { it('should return false because element is undefined', () => { - document.querySelector = jasmine - .createSpy('HTML Element') - .and.returnValue(undefined); + spyOn(windowRef.document, 'querySelector').and.returnValue(undefined); expect(classUnderTest.hasScrollbar('elementMock')).toBe(false); }); @@ -711,27 +711,25 @@ describe('ConfiguratorStorefrontUtilsService', () => { label.style.height = '10px'; }); - spyOnProperty(window, 'innerWidth').and.returnValue(100); + mockedWindow.innerWidth = 100; expect(classUnderTest['isInViewport'](form)).toBe(false); }); - // TODO: CXSPA-8270 - fix failing tests on Azure & GiHub - xit("should return true because window's innerWith is known", () => { + it("should return true because window's innerWith is known", () => { form.style.display = 'flex'; form.style.flexDirection = 'column'; - spyOnProperty(window, 'innerWidth').and.returnValue(1000); + mockedWindow.innerWidth = 1000; expect(classUnderTest['isInViewport'](form)).toBe(true); }); - // TODO: CXSPA-8270 - fix failing tests on Azure & GiHub - xit('should return true because clientWidth of element is known and its right is less than its width', () => { + it('should return true because clientWidth of element is known and its right is less than its width', () => { form.style.display = 'flex'; form.style.flexDirection = 'column'; - spyOnProperty(window, 'innerWidth').and.returnValue(undefined); + mockedWindow.innerWidth = undefined; expect(classUnderTest['isInViewport'](form)).toBe(true); }); @@ -741,7 +739,7 @@ describe('ConfiguratorStorefrontUtilsService', () => { form.style.flexDirection = 'column'; form.style.height = '1000px'; - spyOnProperty(window, 'innerHeight').and.returnValue(undefined); + mockedWindow.innerHeight = undefined; expect(classUnderTest['isInViewport'](form)).toBe(true); }); @@ -765,15 +763,14 @@ describe('ConfiguratorStorefrontUtilsService', () => { expect(classUnderTest['getHeight']('unknown-query')).toBe(0); }); - it('should return zero because form is not im viewport', () => { - spyOnProperty(window, 'innerWidth').and.returnValue(100); + it('should return zero because form is not in viewport', () => { + mockedWindow.innerWidth = 100; expect(classUnderTest['getHeight']('cx-configurator-form')).toBe(0); }); - // TODO: CXSPA-8270 - fix failing tests on Azure & GiHub - xit('should return offsetHeight of the element because form is not im viewport', () => { - spyOnProperty(window, 'innerWidth').and.returnValue(1000); + it('should return offsetHeight of the element because form is not in viewport', () => { + mockedWindow.innerWidth = 1000; expect( classUnderTest['getHeight']('cx-configurator-form') @@ -804,7 +801,7 @@ describe('ConfiguratorStorefrontUtilsService', () => { addToCart = document.createElement('cx-configurator-add-to-cart-button'); document.body.append(addToCart); - spyOnProperty(window, 'innerWidth').and.returnValue(1000); + mockedWindow.innerWidth = 1000; } it('should return zero because isBrowser is undefined', () => { @@ -877,9 +874,7 @@ describe('ConfiguratorStorefrontUtilsService', () => { it('should not ensure visibility of the element', () => { spyOn(windowRef, 'isBrowser').and.returnValue(false); ovMenu = document.createElement('cx-configurator-overview-menu'); - document.querySelector = jasmine - .createSpy('HTML Element') - .and.returnValue(ovMenu); + spyOn(windowRef.document, 'querySelector').and.returnValue(ovMenu); classUnderTest.ensureElementVisible( 'cx-configurator-overview-menu', undefined @@ -988,4 +983,8 @@ describe('ConfiguratorStorefrontUtilsService', () => { expect(classUnderTest.isLastSelected('name', 'code')).toBe(false); }); }); + + function asSpy(f: any) { + return f; + } }); diff --git a/feature-libs/quote/core/services/quote-storefront-utils.service.spec.ts b/feature-libs/quote/core/services/quote-storefront-utils.service.spec.ts index f58273d61e7..dbeb4c399ba 100644 --- a/feature-libs/quote/core/services/quote-storefront-utils.service.spec.ts +++ b/feature-libs/quote/core/services/quote-storefront-utils.service.spec.ts @@ -1,8 +1,20 @@ import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { WindowRef } from '@spartacus/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { WindowRef } from '@spartacus/core'; import { QuoteStorefrontUtilsService } from './quote-storefront-utils.service'; +const mockedWindowTemplate: { innerWidth?: number; innerHeight?: number } = { + innerWidth: 1000, + innerHeight: 1000, +}; + +let mockedWindow = mockedWindowTemplate; +class MockedWindowRef extends WindowRef { + get nativeWindow(): Window | undefined { + return this.isBrowser() ? mockedWindow : undefined; + } +} + @Component({ selector: 'cx-quote', template: ` @@ -20,24 +32,23 @@ describe('QuoteStorefrontUtilsService', () => { let fixture: ComponentFixture; let htmlElem: HTMLElement; let windowRef: WindowRef; - let querySelectorOriginal: any; beforeEach(() => { TestBed.configureTestingModule({ declarations: [MockQuoteComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA], + providers: [{ provide: WindowRef, useClass: MockedWindowRef }], }).compileComponents(); classUnderTest = TestBed.inject(QuoteStorefrontUtilsService); fixture = TestBed.createComponent(MockQuoteComponent); htmlElem = fixture.nativeElement; windowRef = TestBed.inject(WindowRef); + mockedWindow = structuredClone(mockedWindowTemplate); fixture.detectChanges(); - querySelectorOriginal = document.querySelector; }); afterEach(() => { - document.querySelector = querySelectorOriginal; if (htmlElem) { document.body.removeChild(htmlElem); } @@ -52,17 +63,13 @@ describe('QuoteStorefrontUtilsService', () => { it('should get HTML element based on query selector when running in browser and element exists', () => { spyOn(windowRef, 'isBrowser').and.returnValue(true); const theElement = document.createElement('elementMock'); - document.querySelector = jasmine - .createSpy('HTML Element') - .and.returnValue(theElement); + spyOn(windowRef.document, 'querySelector').and.returnValue(theElement); expect(classUnderTest.getElement('elementMock')).toEqual(theElement); }); it('should get null if element does not exist', () => { spyOn(windowRef, 'isBrowser').and.returnValue(true); - document.querySelector = jasmine - .createSpy('HTML Element') - .and.returnValue(null); + spyOn(windowRef.document, 'querySelector').and.returnValue(null); expect(classUnderTest.getElement('unknownElement')).toEqual(null); }); }); @@ -71,9 +78,7 @@ describe('QuoteStorefrontUtilsService', () => { it('should not change styling of HTML element if element does not exist', () => { spyOn(windowRef, 'isBrowser').and.returnValue(true); const element = document.createElement('notExistingElement'); - document.querySelector = jasmine - .createSpy('HTML Element') - .and.returnValue(undefined); + spyOn(windowRef.document, 'querySelector').and.returnValue(undefined); classUnderTest.changeStyling('notExistingElement', 'position', 'sticky'); expect(element.style.position).not.toEqual('sticky'); @@ -82,9 +87,7 @@ describe('QuoteStorefrontUtilsService', () => { it('should change styling of HTML element', () => { spyOn(windowRef, 'isBrowser').and.returnValue(true); const theElement = document.createElement('elementMock'); - document.querySelector = jasmine - .createSpy('HTML Element') - .and.returnValue(theElement); + spyOn(windowRef.document, 'querySelector').and.returnValue(theElement); classUnderTest.changeStyling('elementMock', 'position', 'sticky'); expect(theElement.style.position).toEqual('sticky'); }); @@ -121,27 +124,25 @@ describe('QuoteStorefrontUtilsService', () => { label.style.height = '10px'; }); - spyOnProperty(window, 'innerWidth').and.returnValue(100); + mockedWindow.innerWidth = 100; expect(classUnderTest['isInViewport'](list)).toBe(false); }); - // TODO: CXSPA-8270 - fix failing tests on Azure & GiHub - xit("should return 'true' because window's innerWith is known", () => { + it("should return 'true' because window's innerWith is known", () => { list.style.display = 'flex'; list.style.flexDirection = 'column'; - spyOnProperty(window, 'innerWidth').and.returnValue(1000); + mockedWindow.innerWidth = 1000; expect(classUnderTest['isInViewport'](list)).toBe(true); }); - // TODO: CXSPA-8270 - fix failing tests on Azure & GiHub - xit("should return 'true' because clientWidth of element is known and its right is less than its width", () => { + it("should return 'true' because clientWidth of element is known and its right is less than its width", () => { list.style.display = 'flex'; list.style.flexDirection = 'column'; - spyOnProperty(window, 'innerWidth').and.returnValue(undefined); + mockedWindow.innerWidth = undefined; expect(classUnderTest['isInViewport'](list)).toBe(true); }); @@ -151,7 +152,7 @@ describe('QuoteStorefrontUtilsService', () => { list.style.flexDirection = 'column'; list.style.height = '1000px'; - spyOnProperty(window, 'innerHeight').and.returnValue(undefined); + mockedWindow.innerHeight = undefined; expect(classUnderTest['isInViewport'](list)).toBe(true); }); @@ -176,14 +177,13 @@ describe('QuoteStorefrontUtilsService', () => { }); it('should return zero because component is not in viewport', () => { - spyOnProperty(window, 'innerWidth').and.returnValue(100); + mockedWindow.innerWidth = 100; expect(classUnderTest['getHeight']('cx-quote-list')).toBe(0); }); - // TODO: CXSPA-8270 - fix failing tests on Azure & GiHub - xit('should return offsetHeight of the element because component is not in viewport', () => { - spyOnProperty(window, 'innerWidth').and.returnValue(1000); + it('should return offsetHeight of the element because component is in viewport', () => { + mockedWindow.innerWidth = 1000; expect(classUnderTest['getHeight']('cx-quote-list')).toBeGreaterThan(0); });