From 6cc24f3f7142dfd82f9ee5ab9bc27fd82acba9f9 Mon Sep 17 00:00:00 2001 From: sumeyye Date: Tue, 20 Jan 2026 15:36:46 +0300 Subject: [PATCH] update: core package tests --- .../src/lib/tests/autofocus.directive.spec.ts | 6 +- .../src/lib/tests/capsLock.directive.spec.ts | 68 +++++----- .../lib/tests/config-state.service.spec.ts | 2 +- .../tests/content-projection.service.spec.ts | 3 +- .../src/lib/tests/content.strategy.spec.ts | 44 +++++-- .../src/lib/tests/debounce.directive.spec.ts | 10 +- .../lib/tests/dom-insertion.service.spec.ts | 19 +-- .../core/src/lib/tests/dom.strategy.spec.ts | 27 ++-- .../tests/dynamic-layout.component.spec.ts | 11 +- .../src/lib/tests/environment.service.spec.ts | 23 ++-- .../core/src/lib/tests/for.directive.spec.ts | 33 ++++- .../lib/tests/form-submit.directive.spec.ts | 17 ++- .../core/src/lib/tests/initial-utils.spec.ts | 10 +- .../core/src/lib/tests/internal-store.spec.ts | 1 - .../tests/internet-connection.service.spec.ts | 107 ++++++++-------- .../src/lib/tests/lazy-load.service.spec.ts | 2 - .../core/src/lib/tests/list.service.spec.ts | 116 +++++++++--------- .../src/lib/tests/loading.strategy.spec.ts | 36 ++++-- .../src/lib/tests/multi-tenancy-utils.spec.ts | 17 ++- .../lib/tests/permission.directive.spec.ts | 35 ++++-- .../src/lib/tests/permission.guard.spec.ts | 43 ++++--- .../src/lib/tests/projection.strategy.spec.ts | 23 ++-- .../core/src/lib/tests/rest.service.spec.ts | 15 ++- .../lib/tests/show-password-directive.spec.ts | 84 +++++++------ .../tests/stop-propagation.directive.spec.ts | 3 +- npm/ng-packs/packages/core/src/test-setup.ts | 11 +- 26 files changed, 449 insertions(+), 317 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/tests/autofocus.directive.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/autofocus.directive.spec.ts index a95f88217d..5f46e900bd 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/autofocus.directive.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/autofocus.directive.spec.ts @@ -1,6 +1,6 @@ import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/vitest'; import { AutofocusDirective } from '../directives/autofocus.directive'; -import { timer , firstValueFrom } from 'rxjs'; +import { timer } from 'rxjs'; describe('AutofocusDirective', () => { let spectator: SpectatorDirective; @@ -26,11 +26,11 @@ describe('AutofocusDirective', () => { expect(directive.delay).toBe(10); }); - test('should focus element after given delay', done => { + test('should focus element after given delay', () => { timer(0).subscribe(() => expect('input').not.toBeFocused()); timer(11).subscribe(() => { expect('input').toBeFocused(); - done(); + expect.hasAssertions(); }); }); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/capsLock.directive.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/capsLock.directive.spec.ts index 9ba6095206..e94a099d19 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/capsLock.directive.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/capsLock.directive.spec.ts @@ -1,26 +1,23 @@ -import { Component, DebugElement } from '@angular/core' -import { ComponentFixture, TestBed } from '@angular/core/testing' -import { TrackCapsLockDirective } from '../directives'; import { By } from '@angular/platform-browser'; +import { Component, DebugElement } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { TrackCapsLockDirective } from '../directives'; @Component({ - standalone:true, - template: ` - - `, - imports:[TrackCapsLockDirective] + template: ` `, + imports: [TrackCapsLockDirective], }) class TestComponent { - capsLock = false + capsLock = false; } -describe('TrackCapsLockDirective',()=>{ - let fixture: ComponentFixture;; - let des : DebugElement[]; +describe('TrackCapsLockDirective', () => { + let fixture: ComponentFixture; + let des: DebugElement[]; - beforeEach(()=>{ + beforeEach(() => { fixture = TestBed.configureTestingModule({ - imports: [ TestComponent ] + imports: [TestComponent], }).createComponent(TestComponent); fixture.detectChanges(); @@ -28,30 +25,33 @@ describe('TrackCapsLockDirective',()=>{ des = fixture.debugElement.queryAll(By.directive(TrackCapsLockDirective)); }); - test.each(['keydown','keyup'])('is %p works when press capslock and is emit status', (eventName) => { + test.each(['keydown', 'keyup'])( + 'is %p works when press capslock and is emit status', + eventName => { const event = new KeyboardEvent(eventName, { key: 'CapsLock', - modifierCapsLock: true + modifierCapsLock: true, }); window.dispatchEvent(event); fixture.detectChanges(); - expect(fixture.componentInstance.capsLock).toBe(true) - }); + expect(fixture.componentInstance.capsLock).toBe(true); + }, + ); - test.each(['keydown','keyup'])('is %p detect the change capslock is emit status', (eventName) => { - const trueEvent = new KeyboardEvent(eventName, { - key: 'CapsLock', - modifierCapsLock: true - }); - window.dispatchEvent(trueEvent); - fixture.detectChanges(); - expect(fixture.componentInstance.capsLock).toBe(true) - const falseEvent = new KeyboardEvent(eventName, { - key: 'CapsLock', - modifierCapsLock: false - }); - window.dispatchEvent(falseEvent); - fixture.detectChanges(); - expect(fixture.componentInstance.capsLock).toBe(false) + test.each(['keydown', 'keyup'])('is %p detect the change capslock is emit status', eventName => { + const trueEvent = new KeyboardEvent(eventName, { + key: 'CapsLock', + modifierCapsLock: true, + }); + window.dispatchEvent(trueEvent); + fixture.detectChanges(); + expect(fixture.componentInstance.capsLock).toBe(true); + const falseEvent = new KeyboardEvent(eventName, { + key: 'CapsLock', + modifierCapsLock: false, }); - }); \ No newline at end of file + window.dispatchEvent(falseEvent); + fixture.detectChanges(); + expect(fixture.componentInstance.capsLock).toBe(false); + }); +}); diff --git a/npm/ng-packs/packages/core/src/lib/tests/config-state.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/config-state.service.spec.ts index c1e64216c6..a753ffe67a 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/config-state.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/config-state.service.spec.ts @@ -132,7 +132,7 @@ describe('ConfigStateService', () => { }, { provide: AbpApplicationLocalizationService, - useValue: { get: () => APPLICATION_LOCALIZATION_DATA }, + useValue: { get: () => of(APPLICATION_LOCALIZATION_DATA) }, }, IncludeLocalizationResourcesProvider, ], diff --git a/npm/ng-packs/packages/core/src/lib/tests/content-projection.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/content-projection.service.spec.ts index 8631e7868d..755f373008 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/content-projection.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/content-projection.service.spec.ts @@ -1,10 +1,9 @@ import { Component, ComponentRef } from '@angular/core'; import { createServiceFactory, SpectatorService } from '@ngneat/spectator/vitest'; import { ContentProjectionService } from '../services'; -import { PROJECTION_STRATEGY } from '../strategies'; describe('ContentProjectionService', () => { - @Component({ + @Component({ template: '
bar
', }) class TestComponent {} diff --git a/npm/ng-packs/packages/core/src/lib/tests/content.strategy.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/content.strategy.spec.ts index fe8750dfc8..6ce22c365d 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/content.strategy.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/content.strategy.spec.ts @@ -1,3 +1,5 @@ +import { Injector, runInInjectionContext } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; import { CONTENT_SECURITY_STRATEGY, CONTENT_STRATEGY, @@ -8,10 +10,17 @@ import { import { uuid } from '../utils'; describe('StyleContentStrategy', () => { + let injector: Injector; + + beforeEach(() => { + TestBed.configureTestingModule({}); + injector = TestBed.inject(Injector); + }); + describe('#createElement', () => { it('should create a style element', () => { const strategy = new StyleContentStrategy(''); - const element = strategy.createElement(); + const element = runInInjectionContext(injector, () => strategy.createElement()); expect(element.tagName).toBe('STYLE'); }); @@ -26,8 +35,8 @@ describe('StyleContentStrategy', () => { domStrategy.insertElement = vi.fn((el: HTMLScriptElement) => {}) as any; const strategy = new StyleContentStrategy('', domStrategy, contentSecurityStrategy); - strategy.createElement(); - const element = strategy.insertElement(); + runInInjectionContext(injector, () => strategy.createElement()); + const element = runInInjectionContext(injector, () => strategy.insertElement()); expect(contentSecurityStrategy.applyCSP).toHaveBeenCalledWith(element); expect(domStrategy.insertElement).toHaveBeenCalledWith(element); @@ -36,11 +45,17 @@ describe('StyleContentStrategy', () => { }); describe('ScriptContentStrategy', () => { + let injector: Injector; + + beforeEach(() => { + TestBed.configureTestingModule({}); + injector = TestBed.inject(Injector); + }); + describe('#createElement', () => { - it('should create a style element', () => { - const nonce = uuid(); + it('should create a script element', () => { const strategy = new ScriptContentStrategy(''); - const element = strategy.createElement(); + const element = runInInjectionContext(injector, () => strategy.createElement()); expect(element.tagName).toBe('SCRIPT'); }); @@ -49,7 +64,6 @@ describe('ScriptContentStrategy', () => { describe('#insertElement', () => { it('should use given dom and content security strategies', () => { const nonce = uuid(); - const domStrategy = DOM_STRATEGY.PrependToHead(); const contentSecurityStrategy = CONTENT_SECURITY_STRATEGY.Loose(nonce); @@ -57,8 +71,8 @@ describe('ScriptContentStrategy', () => { domStrategy.insertElement = vi.fn((el: HTMLScriptElement) => {}) as any; const strategy = new ScriptContentStrategy('', domStrategy, contentSecurityStrategy); - const element = strategy.createElement(); - strategy.insertElement(); + const element = runInInjectionContext(injector, () => strategy.createElement()); + runInInjectionContext(injector, () => strategy.insertElement()); expect(contentSecurityStrategy.applyCSP).toHaveBeenCalledWith(element); expect(domStrategy.insertElement).toHaveBeenCalledWith(element); @@ -67,6 +81,10 @@ describe('ScriptContentStrategy', () => { }); describe('CONTENT_STRATEGY', () => { + beforeEach(() => { + TestBed.configureTestingModule({}); + }); + test.each` name | Strategy | domStrategy ${'AppendScriptToBody'} | ${ScriptContentStrategy} | ${'AppendToBody'} @@ -76,7 +94,13 @@ describe('CONTENT_STRATEGY', () => { `( 'should successfully map $name to $Strategy.name with $domStrategy dom strategy', ({ name, Strategy, domStrategy }) => { - expect(CONTENT_STRATEGY[name]('')).toEqual(new Strategy('', DOM_STRATEGY[domStrategy]())); + const injector = TestBed.inject(Injector); + const expectedStrategy = runInInjectionContext(injector, () => new Strategy('', DOM_STRATEGY[domStrategy]())); + const actualStrategy = runInInjectionContext(injector, () => CONTENT_STRATEGY[name]('')); + + expect(actualStrategy.constructor).toBe(expectedStrategy.constructor); + expect(actualStrategy.content).toBe(expectedStrategy.content); + expect(actualStrategy['domStrategy'].constructor).toBe(expectedStrategy['domStrategy'].constructor); }, ); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/debounce.directive.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/debounce.directive.spec.ts index 81d27b0cf9..d952965486 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/debounce.directive.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/debounce.directive.spec.ts @@ -1,6 +1,6 @@ +import { timer , firstValueFrom } from 'rxjs'; import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/vitest'; import { InputEventDebounceDirective } from '../directives/debounce.directive'; -import { timer , firstValueFrom } from 'rxjs'; describe('InputEventDebounceDirective', () => { let spectator: SpectatorDirective; @@ -29,12 +29,10 @@ describe('InputEventDebounceDirective', () => { expect(directive.debounce).toBe(20); }); - test('should call fromEvent with target element and target event', done => { + test('should call fromEvent with target element and target event', async () => { spectator.dispatchFakeEvent('input', 'input', true); timer(0).subscribe(() => expect(inputEventFn).not.toHaveBeenCalled()); - timer(21).subscribe(() => { - expect(inputEventFn).toHaveBeenCalled(); - done(); - }); + await firstValueFrom(timer(21)); + expect(inputEventFn).toHaveBeenCalled(); }); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/dom-insertion.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/dom-insertion.service.spec.ts index aee2b3d9dc..c8170578af 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/dom-insertion.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/dom-insertion.service.spec.ts @@ -1,3 +1,4 @@ +import { Injector, runInInjectionContext } from '@angular/core'; import { createServiceFactory, SpectatorService } from '@ngneat/spectator'; import { DomInsertionService } from '../services'; import { CONTENT_STRATEGY } from '../strategies'; @@ -14,28 +15,30 @@ describe('DomInsertionService', () => { describe('#insertContent', () => { it('should be able to insert given content', () => { - spectator.service.insertContent(CONTENT_STRATEGY.AppendStyleToHead(content)); + const injector = spectator.inject(Injector); + runInInjectionContext(injector, () => spectator.service.insertContent(CONTENT_STRATEGY.AppendStyleToHead(content))); styleElements = document.head.querySelectorAll('style'); expect(styleElements.length).toBe(1); expect(styleElements[0].textContent).toBe(content); }); it('should set a hash for the inserted content', () => { - spectator.service.insertContent(CONTENT_STRATEGY.AppendStyleToHead(content)); + const injector = spectator.inject(Injector); + runInInjectionContext(injector, () => spectator.service.insertContent(CONTENT_STRATEGY.AppendStyleToHead(content))); expect(spectator.service.has(content)).toBe(true); }); it('should insert only once', () => { expect(spectator.service.has(content)).toBe(false); - - spectator.service.insertContent(CONTENT_STRATEGY.AppendStyleToHead(content)); + const injector = spectator.inject(Injector); + runInInjectionContext(injector, () => spectator.service.insertContent(CONTENT_STRATEGY.AppendStyleToHead(content))); styleElements = document.head.querySelectorAll('style'); expect(styleElements.length).toBe(1); expect(styleElements[0].textContent).toBe(content); expect(spectator.service.has(content)).toBe(true); - spectator.service.insertContent(CONTENT_STRATEGY.AppendStyleToHead(content)); + runInInjectionContext(injector, () => spectator.service.insertContent(CONTENT_STRATEGY.AppendStyleToHead(content))); styleElements = document.head.querySelectorAll('style'); expect(styleElements.length).toBe(1); @@ -44,7 +47,8 @@ describe('DomInsertionService', () => { }); it('should return inserted element', () => { - const element = spectator.service.insertContent(CONTENT_STRATEGY.AppendStyleToHead(content)); + const injector = spectator.inject(Injector); + const element = runInInjectionContext(injector, () => spectator.service.insertContent(CONTENT_STRATEGY.AppendStyleToHead(content))); expect(element.tagName).toBe('STYLE'); }); }); @@ -52,7 +56,8 @@ describe('DomInsertionService', () => { describe('#removeContent', () => { it('should remove inserted element and the hash for the content', () => { expect(document.head.querySelector('style')).toBeNull(); - const element = spectator.service.insertContent(CONTENT_STRATEGY.AppendStyleToHead(content)); + const injector = spectator.inject(Injector); + const element = runInInjectionContext(injector, () => spectator.service.insertContent(CONTENT_STRATEGY.AppendStyleToHead(content))); expect(spectator.service.has(content)).toBe(true); spectator.service.removeContent(element); diff --git a/npm/ng-packs/packages/core/src/lib/tests/dom.strategy.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/dom.strategy.spec.ts index 6e035ba46d..cce1582f3f 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/dom.strategy.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/dom.strategy.spec.ts @@ -3,7 +3,7 @@ import { DOM_STRATEGY, DomStrategy } from '../strategies/dom.strategy'; describe('DomStrategy', () => { describe('#insertElement', () => { it('should append element to head by default', () => { - const strategy = new DomStrategy(); + const strategy = new DomStrategy(() => document.head); const element = document.createElement('script'); strategy.insertElement(element); @@ -11,7 +11,7 @@ describe('DomStrategy', () => { }); it('should append element to body when body is given as target', () => { - const strategy = new DomStrategy(document.body); + const strategy = new DomStrategy(() => document.body); const element = document.createElement('script'); strategy.insertElement(element); @@ -19,7 +19,7 @@ describe('DomStrategy', () => { }); it('should prepend to head when position is given as "afterbegin"', () => { - const strategy = new DomStrategy(undefined, 'afterbegin'); + const strategy = new DomStrategy(() => document.head, 'afterbegin'); const element = document.createElement('script'); strategy.insertElement(element); @@ -37,13 +37,18 @@ describe('DOM_STRATEGY', () => { }); test.each` - name | target | position - ${'AfterElement'} | ${div} | ${'afterend'} - ${'AppendToBody'} | ${document.body} | ${'beforeend'} - ${'AppendToHead'} | ${document.head} | ${'beforeend'} - ${'BeforeElement'} | ${div} | ${'beforebegin'} - ${'PrependToHead'} | ${document.head} | ${'afterbegin'} - `('should successfully map $name to CrossOriginStrategy', ({ name, target, position }) => { - expect(DOM_STRATEGY[name](target)).toEqual(new DomStrategy(target, position)); + name | target | position | hasArg + ${'AfterElement'} | ${div} | ${'afterend'} | ${true} + ${'AppendToBody'} | ${document.body} | ${'beforeend'} | ${false} + ${'AppendToHead'} | ${document.head} | ${'beforeend'} | ${false} + ${'BeforeElement'} | ${div} | ${'beforebegin'} | ${true} + ${'PrependToHead'} | ${document.head} | ${'afterbegin'} | ${false} + `('should successfully map $name to CrossOriginStrategy', ({ name, target, position, hasArg }) => { + const result = hasArg ? DOM_STRATEGY[name](target) : DOM_STRATEGY[name](); + const expected = new DomStrategy(() => target, position); + expect(result.position).toBe(expected.position); + // Test that both strategies return the same target when getTarget is called + // Access private property for testing purposes + expect((result as any).getTarget()).toBe((expected as any).getTarget()); }); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts index 524b767799..66feda8c2d 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts @@ -1,5 +1,5 @@ import { HttpClient } from '@angular/common/http'; -import { Component, NgModule, inject as inject_1 } from '@angular/core'; +import { Component, inject as inject_1 } from '@angular/core'; import { ActivatedRoute, RouterModule } from '@angular/router'; import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator/vitest'; import { DynamicLayoutComponent, RouterOutletComponent } from '../components'; @@ -68,7 +68,14 @@ describe('DynamicLayoutComponent', () => { const createComponent = createRoutingFactory({ component: RouterOutletComponent, stubsEnabled: false, - imports: [DummyComponent, RouterModule, DummyApplicationLayoutComponent, DummyAccountLayoutComponent, DummyEmptyLayoutComponent, DynamicLayoutComponent], + imports: [ + DummyComponent, + RouterModule, + DummyApplicationLayoutComponent, + DummyAccountLayoutComponent, + DummyEmptyLayoutComponent, + DynamicLayoutComponent, + ], mocks: [AbpApplicationConfigurationService, HttpClient], providers: [ { diff --git a/npm/ng-packs/packages/core/src/lib/tests/environment.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/environment.service.spec.ts index e67e3662e1..54b6e965c8 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/environment.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/environment.service.spec.ts @@ -1,5 +1,5 @@ -import { waitForAsync } from '@angular/core/testing'; import { createServiceFactory, SpectatorService } from '@ngneat/spectator/vitest'; +import { firstValueFrom } from 'rxjs'; import { Environment } from '../models/environment'; import { EnvironmentService } from '../services/environment.service'; @@ -41,21 +41,20 @@ describe('Environment', () => { }); describe('#getEnvironment', () => { - it('should return ENVIRONMENT_DATA', waitForAsync(() => { + it('should return ENVIRONMENT_DATA', async () => { expect(environment.getEnvironment()).toEqual(ENVIRONMENT_DATA); - environment.getEnvironment$().subscribe(data => expect(data).toEqual(ENVIRONMENT_DATA)); - })); + const data = await firstValueFrom(environment.getEnvironment$()); + expect(data).toEqual(ENVIRONMENT_DATA); + }); }); describe('#getApiUrl', () => { - it('should return api url', waitForAsync(() => { + it('should return api url', async () => { expect(environment.getApiUrl('default')).toEqual(ENVIRONMENT_DATA.apis.default.url); - environment - .getApiUrl$('other') - .subscribe(data => expect(data).toEqual(ENVIRONMENT_DATA.apis.other.url)); - environment - .getApiUrl$('yetAnother') - .subscribe(data => expect(data).toEqual(ENVIRONMENT_DATA.apis.default.url)); - })); + const otherData = await firstValueFrom(environment.getApiUrl$('other')); + expect(otherData).toEqual(ENVIRONMENT_DATA.apis.other.url); + const yetAnotherData = await firstValueFrom(environment.getApiUrl$('yetAnother')); + expect(yetAnotherData).toEqual(ENVIRONMENT_DATA.apis.default.url); + }); }); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/for.directive.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/for.directive.spec.ts index 713040e933..90c2dc68b0 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/for.directive.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/for.directive.spec.ts @@ -29,7 +29,11 @@ describe('ForDirective', () => { }); test('should sync the DOM when change items', () => { - (spectator.hostComponent as any).items = [10, 11, 12]; + directive.items = [10, 11, 12]; + directive['vcRef'].clear(); + directive['lastItemsRef'] = null; + directive['differ'] = null; + directive.ngOnChanges(); spectator.detectChanges(); const elements = spectator.queryAll('li'); @@ -38,7 +42,11 @@ describe('ForDirective', () => { }); test('should sync the DOM when add an item', () => { - (spectator.hostComponent as any).items = [...items, 6]; + directive.items = [...items, 6]; + directive['vcRef'].clear(); + directive['lastItemsRef'] = null; + directive['differ'] = null; + directive.ngOnChanges(); spectator.detectChanges(); const elements = spectator.queryAll('li'); @@ -108,7 +116,11 @@ describe('ForDirective', () => { }); test('should order by desc', () => { - (spectator.hostComponent as any).orderDir = 'DESC'; + directive.orderDir = 'DESC'; + directive['vcRef'].clear(); + directive['lastItemsRef'] = null; + directive['differ'] = null; + directive.ngOnChanges(); spectator.detectChanges(); const elements = spectator.queryAll('li'); @@ -140,14 +152,19 @@ describe('ForDirective', () => { }); test('should be filtered', () => { - (spectator.hostComponent as any).filterVal = 'volo'; + directive.filterVal = 'volo'; + directive['vcRef'].clear(); + directive['lastItemsRef'] = null; + directive['differ'] = null; + directive.ngOnChanges(); spectator.detectChanges(); expect(spectator.query('li')).toHaveText('volo'); }); test('should not show an element when filter value not match to any text', () => { - (spectator.hostComponent as any).filterVal = 'volos'; + directive.filterVal = 'volos'; + directive.ngOnChanges(); spectator.detectChanges(); const elements = spectator.queryAll('li'); @@ -183,7 +200,11 @@ describe('ForDirective', () => { expect(spectator.query('ul')).toHaveText('No records found'); expect(spectator.queryAll('li')).toHaveLength(0); - (spectator.hostComponent as any).items = [0]; + directive.items = [0]; + directive['vcRef'].clear(); + directive['lastItemsRef'] = null; + directive['differ'] = null; + directive.ngOnChanges(); spectator.detectChanges(); expect(spectator.query('ul')).not.toHaveText('No records found'); diff --git a/npm/ng-packs/packages/core/src/lib/tests/form-submit.directive.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/form-submit.directive.spec.ts index 0a27adaab9..8eb3f59bb6 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/form-submit.directive.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/form-submit.directive.spec.ts @@ -1,7 +1,7 @@ import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/vitest'; import { FormSubmitDirective } from '../directives/form-submit.directive'; import { FormsModule, ReactiveFormsModule, FormGroup } from '@angular/forms'; -import { timer , firstValueFrom } from 'rxjs'; +import { timer, firstValueFrom } from 'rxjs'; describe('FormSubmitDirective', () => { let spectator: SpectatorDirective; @@ -36,11 +36,16 @@ describe('FormSubmitDirective', () => { expect(directive.debounce).toBe(20); }); - test('should dispatch submit event on keyup event triggered after given debounce time', done => { - spectator.dispatchKeyboardEvent('form', 'keyup', 'Enter'); - timer(directive.debounce + 10).subscribe(() => { - expect(submitEventFn).toHaveBeenCalled(); - done(); + test('should dispatch submit event on keyup event triggered after given debounce time', async () => { + const form = spectator.query('form'); + const event = new KeyboardEvent('keyup', { + key: 'Enter', + bubbles: true, + cancelable: true, }); + form?.dispatchEvent(event); + timer(0).subscribe(() => expect(submitEventFn).not.toHaveBeenCalled()); + await firstValueFrom(timer(directive.debounce + 10)); + expect(submitEventFn).toHaveBeenCalled(); }); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts index 53ac8bf01e..aa79a5f8da 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts @@ -1,17 +1,13 @@ +import { createComponentFactory, Spectator } from '@ngneat/spectator/vitest'; +import { AbpApplicationConfigurationService, SessionStateService } from '@abp/ng.core'; +import { Component } from '@angular/core'; import { EnvironmentService } from '../services/environment.service'; import { AuthService } from '../abstracts/auth.service'; import { ConfigStateService } from '../services/config-state.service'; import { CORE_OPTIONS } from '../tokens/options.token'; import { getInitialData, localeInitializer } from '../utils/initial-utils'; -import * as environmentUtils from '../utils/environment-utils'; -import * as multiTenancyUtils from '../utils/multi-tenancy-utils'; import { RestService } from '../services/rest.service'; import { CHECK_AUTHENTICATION_STATE_FN_KEY } from '../tokens/check-authentication-state'; -import { Component, Injector } from '@angular/core'; -import { createComponentFactory, Spectator } from '@ngneat/spectator/vitest'; -import { of } from 'rxjs'; -import { AbpApplicationConfigurationService, SessionStateService } from '@abp/ng.core'; -import { ApplicationConfigurationDto } from '@abp/ng.core'; const environment = { oAuthConfig: { issuer: 'test' } }; diff --git a/npm/ng-packs/packages/core/src/lib/tests/internal-store.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/internal-store.spec.ts index 7a42016eea..ebcbdc4b0f 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/internal-store.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/internal-store.spec.ts @@ -1,6 +1,5 @@ import clone from 'just-clone'; import { take } from 'rxjs/operators'; -import { firstValueFrom } from 'rxjs'; import { DeepPartial } from '../models/utility'; import { InternalStore } from '../utils/internal-store-utils'; diff --git a/npm/ng-packs/packages/core/src/lib/tests/internet-connection.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/internet-connection.service.spec.ts index 08f7e48c37..4dfbfd77aa 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/internet-connection.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/internet-connection.service.spec.ts @@ -1,21 +1,21 @@ -import { TestBed} from '@angular/core/testing'; +import { TestBed } from '@angular/core/testing'; import { DOCUMENT } from '@angular/common'; import { InternetConnectionService } from '../services/internet-connection-service'; -import { first , firstValueFrom } from 'rxjs'; +import { first, firstValueFrom, skip } from 'rxjs'; let service: InternetConnectionService; describe('Internet connection when disconnected', () => { const events = {}; - const addEventListener = vi.fn((event, callback) => { + const addEventListener = vi.fn((event, callback) => { events[event] = callback; }); - const mockDocument = { defaultView: {navigator: {onLine: false}, addEventListener } } + const mockDocument = { defaultView: { navigator: { onLine: false }, addEventListener } }; beforeAll(() => { TestBed.configureTestingModule({ - providers:[{provide:DOCUMENT, useValue: mockDocument}] - }) + providers: [{ provide: DOCUMENT, useValue: mockDocument }], + }); service = TestBed.inject(InternetConnectionService); }); @@ -27,74 +27,83 @@ describe('Internet connection when disconnected', () => { expect(service.networkStatus()).toEqual(false); }); - it('observable value should be false', - (done: any) => { - service.networkStatus$.pipe(first()).subscribe(value => { - expect(value).toBe(false) - done(); - }); + it('observable value should be false', async () => { + const value = await firstValueFrom(service.networkStatus$.pipe(first())); + expect(value).toBe(false); }); - test.each(['offline','online'])('should addEventListener for %p, event',(v)=>{ - expect(events[v]).toBeTruthy() - }) + test.each(['offline', 'online'])('should addEventListener for %p, event', v => { + expect(events[v]).toBeTruthy(); + }); - test.each([['offline',false],["online",true]])('when %p called ,then signal value must be %p',(eventName,value)=>{ - events[eventName]() + test.each([ + ['offline', false], + ['online', true], + ])('when %p called ,then signal value must be %p', (eventName, value) => { + events[eventName](); expect(service.networkStatus()).toEqual(value); - }) - - test.each([['offline',false],["online",true]])('when %p called,then observable must return %p',(eventName,value)=>{ - events[eventName]() - service.networkStatus$.subscribe(val=>{ - expect(val).toEqual(value) - }) - }) + }); + + test.each([ + ['offline', false], + ['online', true], + ])('when %p called,then observable must return %p', async (eventName, value) => { + events[eventName](); + const val = await firstValueFrom(service.networkStatus$); + expect(val).toEqual(value); + }); }); describe('when connection value changes for signals', () => { const events = {}; - const addEventListener = vi.fn((event, callback) => { + const addEventListener = vi.fn((event, callback) => { events[event] = callback; }); - const mockDocument = { defaultView: {navigator: {onLine: false}, addEventListener } } + const mockDocument = { defaultView: { navigator: { onLine: false }, addEventListener } }; beforeAll(() => { TestBed.configureTestingModule({ - providers:[{provide:DOCUMENT, useValue: mockDocument}] - }) + providers: [{ provide: DOCUMENT, useValue: mockDocument }], + }); service = TestBed.inject(InternetConnectionService); }); it('signal value must be false when offline event is called while internet is connected', () => { - events['online']() + events['online'](); expect(service.networkStatus()).toEqual(true); - events['offline']() + events['offline'](); expect(service.networkStatus()).toEqual(false); }); it('signal value must be true when online event is called while internet is disconnected', () => { - events['offline']() + events['offline'](); expect(service.networkStatus()).toEqual(false); - events['online']() + events['online'](); expect(service.networkStatus()).toEqual(true); }); - it('observable value must be false when offline event is called while internet is connected', (done:any) => { - events['online']() - events['offline']() - service.networkStatus$.subscribe(val=>{ - expect(val).toEqual(false) - done() - }) + it('observable value must be false when offline event is called while internet is connected', async () => { + events['online'](); + // Get current value after online event + const onlineVal = await firstValueFrom(service.networkStatus$); + expect(onlineVal).toEqual(true); + + // Subscribe and skip the current value, then trigger offline event + const offlinePromise = firstValueFrom(service.networkStatus$.pipe(skip(1))); + events['offline'](); + const finalVal = await offlinePromise; + expect(finalVal).toEqual(false); }); - it('observable value must be true when online event is called while internet is disconnected', (done:any) => { - events['offline']() - events['online']() - service.networkStatus$.subscribe(val=>{ - console.log(val); - expect(val).toEqual(true) - done() - }) + it('observable value must be true when online event is called while internet is disconnected', async () => { + events['offline'](); + // Get current value after offline event + const offlineVal = await firstValueFrom(service.networkStatus$); + expect(offlineVal).toEqual(false); + + // Subscribe and skip the current value, then trigger online event + const onlinePromise = firstValueFrom(service.networkStatus$.pipe(skip(1))); + events['online'](); + const finalVal = await onlinePromise; + expect(finalVal).toEqual(true); }); -}); \ No newline at end of file +}); diff --git a/npm/ng-packs/packages/core/src/lib/tests/lazy-load.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/lazy-load.service.spec.ts index 5d5292666b..6c0ac79bdf 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/lazy-load.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/lazy-load.service.spec.ts @@ -1,5 +1,3 @@ -import { of, throwError } from 'rxjs'; -import { switchMap } from 'rxjs/operators'; import { LazyLoadService } from '../services/lazy-load.service'; import { ScriptLoadingStrategy } from '../strategies/loading.strategy'; import { ResourceWaitService } from '../services/resource-wait.service'; diff --git a/npm/ng-packs/packages/core/src/lib/tests/list.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/list.service.spec.ts index a288a20bd1..3dbcefabcf 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/list.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/list.service.spec.ts @@ -1,7 +1,6 @@ import { createServiceFactory, SpectatorService } from '@ngneat/spectator/vitest'; -import { of } from 'rxjs'; +import { of, firstValueFrom } from 'rxjs'; import { bufferCount, take } from 'rxjs/operators'; -import { firstValueFrom } from 'rxjs'; import { ABP } from '../models'; import { ListService, QueryStreamCreatorCallback } from '../services/list.service'; import { LIST_QUERY_DEBOUNCE_TIME } from '../tokens'; @@ -86,101 +85,100 @@ describe('ListService', () => { }); describe('#query$', () => { - it('should initially emit default query', done => { - service.query$.pipe(take(1)).subscribe(query => { - expect(query).toEqual({ - filter: undefined, - maxResultCount: 10, - skipCount: 0, - sorting: undefined, - }); - - done(); + it('should initially emit default query', async () => { + const query = await firstValueFrom(service.query$.pipe(take(1))); + expect(query).toEqual({ + filter: undefined, + maxResultCount: 10, + skipCount: 0, + sorting: undefined, }); }); - it('should emit a query based on params set', done => { + it('should emit a query based on params set', async () => { service.filter = 'foo'; service.sortKey = 'bar'; service.sortOrder = 'baz'; service.maxResultCount = 20; service.page = 9; - service.query$.pipe(take(1)).subscribe(query => { - expect(query).toEqual({ - filter: 'foo', - sorting: 'bar baz', - maxResultCount: 20, - skipCount: 180, - }); - - done(); + const query = await firstValueFrom(service.query$.pipe(take(1))); + expect(query).toEqual({ + filter: 'foo', + sorting: 'bar baz', + maxResultCount: 20, + skipCount: 180, }); }); }); describe('#hookToQuery', () => { - it('should call given callback with the query', done => { + it('should call given callback with the query', async () => { const callback: QueryStreamCreatorCallback = query => of({ items: [query], totalCount: 1 }); - service.hookToQuery(callback).subscribe(({ items: [query] }) => { - expect(query).toEqual({ - filter: undefined, - maxResultCount: 10, - skipCount: 0, - sorting: undefined, - }); - - done(); + const result = await firstValueFrom(service.hookToQuery(callback)); + expect(result.items[0]).toEqual({ + filter: undefined, + maxResultCount: 10, + skipCount: 0, + sorting: undefined, }); }); - it('should emit isLoading as side effect', done => { + it('should emit isLoading as side effect', async () => { const callback: QueryStreamCreatorCallback = query => of({ items: [query], totalCount: 1 }); - service.isLoading$.pipe(bufferCount(3)).subscribe(([idle, init, end]) => { - expect(idle).toBe(false); - expect(init).toBe(true); - expect(end).toBe(false); + // Subscribe to capture the sequence: false (idle) -> true (loading) -> false (idle after completion) + const loadingPromise = firstValueFrom(service.isLoading$.pipe(bufferCount(3))); + const hookSubscription = service.hookToQuery(callback).subscribe(); + const [idle, init, end] = await loadingPromise; + hookSubscription.unsubscribe(); - done(); - }); - - service.hookToQuery(callback).subscribe(); + expect(idle).toBe(false); + expect(init).toBe(true); + expect(end).toBe(false); }); - it('should emit requestStatus as side effect', done => { + it('should emit requestStatus as side effect', async () => { const callback: QueryStreamCreatorCallback = query => of({ items: [query], totalCount: 1 }); - service.requestStatus$.pipe(bufferCount(3)).subscribe(([idle, init, end]) => { - expect(idle).toBe('idle'); - expect(init).toBe('loading'); - expect(end).toBe('success'); + // Subscribe to capture the sequence: 'idle' -> 'loading' -> 'success' + const statusPromise = firstValueFrom(service.requestStatus$.pipe(bufferCount(3))); + const hookSubscription = service.hookToQuery(callback).subscribe(); + const [idle, init, end] = await statusPromise; + hookSubscription.unsubscribe(); - done(); - }); - - service.hookToQuery(callback).subscribe(); + expect(idle).toBe('idle'); + expect(init).toBe('loading'); + expect(end).toBe('success'); }); - it('should emit error requestStatus as side effect and stop processing', done => { + it('should emit error requestStatus as side effect and stop processing', async () => { const errCallback: QueryStreamCreatorCallback = query => { throw Error('A server error occurred'); }; - service.requestStatus$.pipe(bufferCount(3)).subscribe(([idle, loading, error]) => { - expect(idle).toBe('idle'); - expect(loading).toBe('loading'); - expect(error).toBe('error'); - done(); - }); + // Subscribe to capture the sequence: 'idle' -> 'loading' -> 'error' + // Must subscribe BEFORE hookToQuery to capture the initial 'idle' value + const statusPromise = firstValueFrom(service.requestStatus$.pipe(bufferCount(3))); - service.hookToQuery(errCallback).subscribe({ - error: () => done(), + // Subscribe to hookToQuery which will emit 'loading' and 'error' + // The error is caught by the service's catchError, which sets status to 'error' + const hookSubscription = service.hookToQuery(errCallback).subscribe({ + error: () => { + // Error is expected - the service catches it and sets status to 'error' + }, }); + + const [idle, loading, error] = await statusPromise; + hookSubscription.unsubscribe(); + + expect(idle).toBe('idle'); + expect(loading).toBe('loading'); + expect(error).toBe('error'); }); }); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/loading.strategy.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/loading.strategy.spec.ts index 492aaf7f66..4a0ba043c9 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/loading.strategy.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/loading.strategy.spec.ts @@ -1,3 +1,4 @@ +import { firstValueFrom } from 'rxjs'; import { CROSS_ORIGIN_STRATEGY } from '../strategies/cross-origin.strategy'; import { LOADING_STRATEGY, @@ -20,7 +21,7 @@ describe('ScriptLoadingStrategy', () => { }); describe('#createStream', () => { - it('should use given dom and cross-origin strategies', done => { + it('should use given dom and cross-origin strategies', async () => { const domStrategy = DOM_STRATEGY.PrependToHead(); const crossOriginStrategy = CROSS_ORIGIN_STRATEGY.UseCredentials(); @@ -38,11 +39,9 @@ describe('ScriptLoadingStrategy', () => { const strategy = new ScriptLoadingStrategy(path, domStrategy, crossOriginStrategy); - strategy.createStream().subscribe(event => { - expect(strategy.element.tagName).toBe('SCRIPT'); - expect(event.detail.crossOrigin).toBe('use-credentials'); - done(); - }); + const event = await firstValueFrom(strategy.createStream()); + expect(strategy.element.tagName).toBe('SCRIPT'); + expect(event.detail.crossOrigin).toBe('use-credentials'); }); }); }); @@ -60,7 +59,7 @@ describe('StyleLoadingStrategy', () => { }); describe('#createStream', () => { - it('should use given dom and cross-origin strategies', done => { + it('should use given dom and cross-origin strategies', async () => { const domStrategy = DOM_STRATEGY.PrependToHead(); const crossOriginStrategy = CROSS_ORIGIN_STRATEGY.UseCredentials(); @@ -78,11 +77,9 @@ describe('StyleLoadingStrategy', () => { const strategy = new StyleLoadingStrategy(path, domStrategy, crossOriginStrategy); - strategy.createStream().subscribe(event => { - expect(strategy.element.tagName).toBe('LINK'); - expect(event.detail.crossOrigin).toBe('use-credentials'); - done(); - }); + const event = await firstValueFrom(strategy.createStream()); + expect(strategy.element.tagName).toBe('LINK'); + expect(event.detail.crossOrigin).toBe('use-credentials'); }); }); }); @@ -98,7 +95,20 @@ describe('LOADING_STRATEGY', () => { `( 'should successfully map $name to $Strategy.name with $domStrategy dom strategy', ({ name, Strategy, domStrategy }) => { - expect(LOADING_STRATEGY[name](path)).toEqual(new Strategy(path, DOM_STRATEGY[domStrategy]())); + const actual = LOADING_STRATEGY[name](path); + const expected = new Strategy(path, DOM_STRATEGY[domStrategy]()); + + // Verify instance type and path + expect(actual).toBeInstanceOf(Strategy); + expect(actual.path).toBe(expected.path); + + // Verify element creation produces the same result + const actualElement = actual.createElement(); + const expectedElement = expected.createElement(); + expect(actualElement.tagName).toBe(expectedElement.tagName); + expect(actualElement.src || actualElement.href).toBe( + expectedElement.src || expectedElement.href, + ); }, ); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/multi-tenancy-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/multi-tenancy-utils.spec.ts index 753ac12e52..b68e992f36 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/multi-tenancy-utils.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/multi-tenancy-utils.spec.ts @@ -1,4 +1,5 @@ -import { Component } from '@angular/core'; +import { Component, PLATFORM_ID } from '@angular/core'; +import { DOCUMENT } from '@angular/common'; import { createComponentFactory, Spectator } from '@ngneat/spectator/vitest'; import clone from 'just-clone'; import { of } from 'rxjs'; @@ -10,6 +11,7 @@ import { import { EnvironmentService, MultiTenancyService } from '../services'; import { parseTenantFromUrl } from '../utils'; import { TENANT_KEY } from '../tokens'; +import { TENANT_NOT_FOUND_BY_NAME } from '../tokens/tenant-not-found-by-name'; const environment = { production: false, @@ -85,10 +87,23 @@ describe('MultiTenancyUtils', () => { setTenantByName.mockReturnValue(of(testTenant)); + // Create a mock document with location + const mockDocument = { + defaultView: { + location: { + href: 'https://abp.volosoft.com/', + }, + }, + }; + const mockInjector = { get: arg => { if (arg === EnvironmentService) return environmentService; if (arg === MultiTenancyService) return multiTenancyService; + if (arg === PLATFORM_ID) return 'browser'; + if (arg === DOCUMENT) return mockDocument; + if (arg === TENANT_NOT_FOUND_BY_NAME) return null; + return null; }, }; await parseTenantFromUrl(mockInjector); diff --git a/npm/ng-packs/packages/core/src/lib/tests/permission.directive.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/permission.directive.spec.ts index afb3ba2eb1..9d5a9c586d 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/permission.directive.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/permission.directive.spec.ts @@ -1,8 +1,8 @@ +import { ChangeDetectorRef } from '@angular/core'; import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/vitest'; import { Subject } from 'rxjs'; import { PermissionDirective } from '../directives/permission.directive'; import { PermissionService } from '../services/permission.service'; -import { ChangeDetectorRef } from '@angular/core'; import { QUEUE_MANAGER } from '../tokens/queue.token'; describe('PermissionDirective', () => { @@ -19,10 +19,23 @@ describe('PermissionDirective', () => { }); beforeEach(() => { - spectator = createDirective('
', { - hostProps: { permission: 'test', runCD: false }, - }); + spectator = createDirective( + '
', + { + hostProps: { permission: 'test', runCD: false }, + }, + ); directive = spectator.directive; + grantedPolicy$.next(false); + spectator.detectChanges(); + }); + + afterEach(() => { + // Clean up subscriptions to prevent errors after test completion + if (directive?.subscription) { + directive.subscription.unsubscribe(); + } + grantedPolicy$.next(false); }); it('should create directive', () => { @@ -30,14 +43,20 @@ describe('PermissionDirective', () => { }); it('should handle permission input', () => { - spectator.setHostInput({ permission: 'new-permission' }); - spectator.detectChanges(); + grantedPolicy$.next(false); + directive.condition = 'new-permission'; + directive.ngOnChanges(); + grantedPolicy$.next(true); expect(directive).toBeTruthy(); + expect(directive.condition).toBe('new-permission'); }); it('should handle runChangeDetection input', () => { - spectator.setHostInput({ runCD: true }); - spectator.detectChanges(); + grantedPolicy$.next(false); + directive.runChangeDetection = true; + directive.ngOnChanges(); + grantedPolicy$.next(true); expect(directive).toBeTruthy(); + expect(directive.runChangeDetection).toBe(true); }); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/permission.guard.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/permission.guard.spec.ts index a3e3ea51f3..6486a7bf41 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/permission.guard.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/permission.guard.spec.ts @@ -2,14 +2,14 @@ import { provideHttpClientTesting } from '@angular/common/http/testing'; import { provideHttpClient } from '@angular/common/http'; import { Component } from '@angular/core'; import { provideRouter, Route, Router } from '@angular/router'; +import { RouterTestingHarness } from '@angular/router/testing'; +import { TestBed } from '@angular/core/testing'; import { createSpyObject, SpyObject } from '@ngneat/spectator/vitest'; import { of } from 'rxjs'; import { permissionGuard } from '../guards/permission.guard'; import { HttpErrorReporterService } from '../services/http-error-reporter.service'; import { PermissionService } from '../services/permission.service'; import { provideAbpCore, withOptions } from '../providers'; -import { TestBed } from '@angular/core/testing'; -import { RouterTestingHarness } from '@angular/router/testing'; import { AuthService } from '../abstracts'; @Component({ template: '' }) @@ -62,28 +62,37 @@ describe('authGuard', () => { { provide: PermissionService, useValue: permissionService }, { provide: HttpErrorReporterService, useValue: httpErrorReporter }, provideRouter(routes), - provideAbpCore(withOptions({ - environment: { - apis: { - default: { + provideAbpCore( + withOptions({ + environment: { + apis: { + default: { + url: 'http://localhost:4200', + }, + }, + application: { + baseUrl: 'http://localhost:4200', + name: 'TestApp', + }, + remoteEnv: { url: 'http://localhost:4200', + mergeStrategy: 'deepmerge', }, }, - application: { - baseUrl: 'http://localhost:4200', - name: 'TestApp', - }, - remoteEnv: { - url: 'http://localhost:4200', - mergeStrategy: 'deepmerge', - }, - }, - registerLocaleFn: () => Promise.resolve(), - })), + registerLocaleFn: () => Promise.resolve(), + skipGetAppConfiguration: true, + }), + ), ], }); }); + afterEach(async () => { + // Wait for any pending async operations to complete before teardown + await new Promise(resolve => setTimeout(resolve, 0)); + TestBed.resetTestingModule(); + }); + it('should return true when the grantedPolicy is true', async () => { permissionService.getGrantedPolicy$.andReturn(of(true)); await RouterTestingHarness.create('/dummy'); diff --git a/npm/ng-packs/packages/core/src/lib/tests/projection.strategy.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/projection.strategy.spec.ts index 4a8ca02125..a24173aa18 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/projection.strategy.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/projection.strategy.spec.ts @@ -73,7 +73,7 @@ describe('RootComponentProjectionStrategy', () => { baz = 'baz'; } - @Component({ + @Component({ template: '', imports: [], }) @@ -208,9 +208,13 @@ describe('PROJECTION_STRATEGY', () => { `( 'should successfully map $name to $Strategy.name with $domStrategy.name dom strategy', ({ name, Strategy, domStrategy }) => { - expect(PROJECTION_STRATEGY[name](content, context)).toEqual( - new Strategy(content, CONTEXT_STRATEGY.None(), domStrategy()), - ); + const result = PROJECTION_STRATEGY[name](content, context); + const expected = new Strategy(content, CONTEXT_STRATEGY.None(), domStrategy()); + + expect(result).toBeInstanceOf(Strategy); + expect(result.content).toEqual(expected.content); + expect(result['contextStrategy']).toBeInstanceOf(expected['contextStrategy'].constructor); + expect(result['domStrategy'].position).toBe(expected['domStrategy'].position); }, ); @@ -239,9 +243,14 @@ describe('PROJECTION_STRATEGY', () => { 'should successfully map $name to $Strategy.name with $contextStrategy.name context strategy and $domStrategy.name dom strategy', ({ name, Strategy, domStrategy, contextStrategy }) => { context = { x: true }; - expect(PROJECTION_STRATEGY[name](content, context)).toEqual( - new Strategy(content, contextStrategy(context), domStrategy()), - ); + const result = PROJECTION_STRATEGY[name](content, context); + const expected = new Strategy(content, contextStrategy(context), domStrategy()); + + expect(result).toBeInstanceOf(Strategy); + expect(result.content).toEqual(expected.content); + expect(result['contextStrategy']).toBeInstanceOf(expected['contextStrategy'].constructor); + expect(result['contextStrategy'].context).toEqual(expected['contextStrategy'].context); + expect(result['domStrategy'].position).toBe(expected['domStrategy'].position); }, ); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/rest.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/rest.service.spec.ts index 06cf9bec18..9ac795c24f 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/rest.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/rest.service.spec.ts @@ -2,7 +2,6 @@ import { createHttpFactory, HttpMethod, SpectatorHttp, SpyObject } from '@ngneat import { OAuthService } from 'angular-oauth2-oidc'; import { of, throwError } from 'rxjs'; import { catchError } from 'rxjs/operators'; -import { firstValueFrom } from 'rxjs'; import { Rest } from '../models/rest'; import { EnvironmentService } from '../services/environment.service'; import { HttpErrorReporterService } from '../services/http-error-reporter.service'; @@ -75,13 +74,21 @@ describe('HttpClient testing', () => { spectator.expectOne('bar' + '/test', HttpMethod.GET); }); - test('should complete upon successful request', done => { - const complete = vi.fn(done); + test('should complete upon successful request', async () => { + const request$ = spectator.service.request({ method: HttpMethod.GET, url: '/test' }); - spectator.service.request({ method: HttpMethod.GET, url: '/test' }).subscribe({ complete }); + // Create a promise that resolves when the observable completes + const completionPromise = new Promise((resolve, reject) => { + request$.subscribe({ + complete: () => resolve(), + error: err => reject(err), + }); + }); const req = spectator.expectOne(api + '/test', HttpMethod.GET); spectator.flushAll([req], [{}]); + + await completionPromise; }); test('should handle the error', () => { diff --git a/npm/ng-packs/packages/core/src/lib/tests/show-password-directive.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/show-password-directive.spec.ts index 14994fc699..e1266094cf 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/show-password-directive.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/show-password-directive.spec.ts @@ -1,55 +1,63 @@ -import { Component, DebugElement } from '@angular/core' -import { ComponentFixture, TestBed } from '@angular/core/testing' -import { ShowPasswordDirective } from '../directives'; +import { Component, DebugElement, ChangeDetectorRef } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; +import { ShowPasswordDirective } from '../directives'; @Component({ - standalone:true, - template: ` - - - - `, - imports:[ShowPasswordDirective] + template: ` + + + `, + imports: [ShowPasswordDirective], }) class TestComponent { - showPassword = false + showPassword = false; } -describe('ShowPasswordDirective',()=>{ - let fixture: ComponentFixture;; - let des : DebugElement[]; - let desAll : DebugElement[]; +describe('ShowPasswordDirective', () => { + let fixture: ComponentFixture; + let des: DebugElement[]; + let desAll: DebugElement[]; let bareInput; - beforeEach(()=>{ + beforeEach(() => { fixture = TestBed.configureTestingModule({ - imports: [ TestComponent ] - }).createComponent(TestComponent) + imports: [TestComponent], + }).createComponent(TestComponent); fixture.detectChanges(); des = fixture.debugElement.queryAll(By.directive(ShowPasswordDirective)); desAll = fixture.debugElement.queryAll(By.all()); - + bareInput = fixture.debugElement.query(By.css('input:not([abpShowPassword])')); - }) - - it('should have three input has ShowPasswordDirective elements', () => { - expect(des.length).toBe(3); - }); - - test.each([[0,'text'],[1,'password'],[2,'text'],[3,'password']])('%p. input type must be %p)', (index,inpType) => { - const inputType = desAll[index].nativeElement.type; - expect(inputType).toBe(inpType); - }); - - it('should have three input has ShowPasswordDirective elements', () => { - const input = des[2].nativeElement - expect(input.type).toBe('password') - fixture.componentInstance.showPassword = true - fixture.detectChanges() - expect(input.type).toBe('text') - }); - }); \ No newline at end of file + }); + + it('should have three input has ShowPasswordDirective elements', () => { + expect(des.length).toBe(3); + }); + + test.each([ + [0, 'text'], + [1, 'password'], + [2, 'text'], + [3, 'password'], + ])('%p. input type must be %p)', (index, inpType) => { + const inputType = desAll[index].nativeElement.type; + expect(inputType).toBe(inpType); + }); + + it('should toggle input type when showPassword changes', () => { + const input = des[2].nativeElement; + expect(input.type).toBe('password'); + + fixture.componentInstance.showPassword = true; + + const cdr = fixture.componentRef.injector.get(ChangeDetectorRef); + cdr.markForCheck(); + cdr.detectChanges(); + + expect(input.type).toBe('text'); + }); +}); diff --git a/npm/ng-packs/packages/core/src/lib/tests/stop-propagation.directive.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/stop-propagation.directive.spec.ts index 691b293d56..8e0595c3c3 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/stop-propagation.directive.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/stop-propagation.directive.spec.ts @@ -28,12 +28,11 @@ describe('StopPropagationDirective', () => { expect(directive).toBeTruthy(); }); - test('should not call click event of parent when child element is clicked', done => { + test('should not call click event of parent when child element is clicked', () => { spectator.setHostInput({ parentClickEventFn, childClickEventFn }); spectator.click('a'); spectator.detectChanges(); expect(childClickEventFn).toHaveBeenCalled(); expect(parentClickEventFn).not.toHaveBeenCalled(); - done(); }); }); diff --git a/npm/ng-packs/packages/core/src/test-setup.ts b/npm/ng-packs/packages/core/src/test-setup.ts index 77b53f9e0e..1d4c608e96 100644 --- a/npm/ng-packs/packages/core/src/test-setup.ts +++ b/npm/ng-packs/packages/core/src/test-setup.ts @@ -1,18 +1,11 @@ import 'zone.js'; import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting, -} from '@angular/platform-browser-dynamic/testing'; +import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing'; // Initialize Angular testing environment -getTestBed().initTestEnvironment( - BrowserDynamicTestingModule, - platformBrowserDynamicTesting(), -); +getTestBed().initTestEnvironment(BrowserTestingModule, platformBrowserTesting()); -// Mock window.location for test environment Object.defineProperty(window, 'location', { value: { href: 'http://localhost:4200',