From 2a6d34ce78af61065e13d1d4d806a29a26063e86 Mon Sep 17 00:00:00 2001 From: sumeyye Date: Mon, 29 Jun 2026 17:15:02 +0300 Subject: [PATCH 1/8] fix: merger problem for the versions --- npm/ng-packs/package.json | 51 ++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index f27e905f84..1151323ac9 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -32,7 +32,7 @@ "dep-graph": "nx dep-graph", "help": "nx help", "build:schematics": "cd scripts && yarn && yarn build:schematics && cd ..", - "dev:schematics": "tsc -p packages/schematics/tsconfig.lib.json -w", + "dev:schematics": "tsc -p packages/schematics/tsconfig.json -w", "mock:schematics": "cd scripts/mock-schematic && yarn && yarn start", "debug:schematics": "./node_modules/.bin/nx g ./packages/schematics/src/collection.json:proxy-add --module identity --apiName __default --source __default --target __default --url https://localhost:44305 --serviceType application --entryPoint __default ", "debug:schematics-dist": "./node_modules/.bin/ng g ./dist/packages/schematics/collection.json:proxy-add --module __default --apiName __default --source __default --target __default --url http://localhost:4300 --service-type application --entryPoint __default", @@ -49,30 +49,31 @@ "private": true, "devDependencies": { "@abp/ng.theme.lepton-x": "~5.5.0-rc.4", + "@abp/ng.schematics": "~10.5.0-rc.4", "@abp/utils": "~10.5.0-rc.4", - "@angular-devkit/build-angular": "~21.2.0", - "@angular-devkit/core": "~21.2.0", - "@angular-devkit/schematics": "~21.2.0", - "@angular-devkit/schematics-cli": "~21.2.0", - "@angular-eslint/eslint-plugin": "~21.2.0", - "@angular-eslint/eslint-plugin-template": "~21.2.0", - "@angular-eslint/template-parser": "~21.2.0", - "@angular/animations": "~21.2.0", - "@angular/aria": "~21.2.0", - "@angular/build": "~21.2.0", - "@angular/cli": "~21.2.0", - "@angular/common": "~21.2.0", - "@angular/compiler": "~21.2.0", - "@angular/compiler-cli": "~21.2.0", - "@angular/core": "~21.2.0", - "@angular/forms": "~21.2.0", - "@angular/language-service": "~21.2.0", - "@angular/localize": "~21.2.0", - "@angular/platform-browser": "~21.2.0", - "@angular/platform-browser-dynamic": "~21.2.0", - "@angular/platform-server": "~21.2.0", - "@angular/router": "~21.2.0", - "@angular/ssr": "~21.2.0", + "@angular-devkit/build-angular": "~22.0.0", + "@angular-devkit/core": "~22.0.0", + "@angular-devkit/schematics": "~22.0.0", + "@angular-devkit/schematics-cli": "~22.0.0", + "@angular-eslint/eslint-plugin": "~22.0.0", + "@angular-eslint/eslint-plugin-template": "~22.0.0", + "@angular-eslint/template-parser": "~22.0.0", + "@angular/animations": "~22.0.0", + "@angular/aria": "~22.0.0", + "@angular/build": "~22.0.0", + "@angular/cli": "~22.0.0", + "@angular/common": "~22.0.0", + "@angular/compiler": "~22.0.0", + "@angular/compiler-cli": "~22.0.0", + "@angular/core": "~22.0.0", + "@angular/forms": "~22.0.0", + "@angular/language-service": "~22.0.0", + "@angular/localize": "~22.0.0", + "@angular/platform-browser": "~22.0.0", + "@angular/platform-browser-dynamic": "~22.0.0", + "@angular/platform-server": "~22.0.0", + "@angular/router": "~22.0.0", + "@angular/ssr": "~22.0.0", "@fortawesome/fontawesome-free": "^6.0.0", "@ng-bootstrap/ng-bootstrap": "~20.0.0", "@ngneat/spectator": "~19.6.2", @@ -159,4 +160,4 @@ "dependencies": { "openid-client": "^6.6.4" } -} +} \ No newline at end of file From ba40a4a624e1919109e9f459e25af99a4e849e4a Mon Sep 17 00:00:00 2001 From: sumeyye Date: Mon, 29 Jun 2026 17:15:24 +0300 Subject: [PATCH 2/8] fix: storage check for the local storage service --- .../src/lib/services/local-storage.service.ts | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/services/local-storage.service.ts b/npm/ng-packs/packages/core/src/lib/services/local-storage.service.ts index ebe47955f2..cf0687407d 100644 --- a/npm/ng-packs/packages/core/src/lib/services/local-storage.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/local-storage.service.ts @@ -10,35 +10,36 @@ export class AbpLocalStorageService implements Storage { constructor() { } [name: string]: any; + + private get storage(): Storage | null { + if (!isPlatformBrowser(this.platformId) || typeof window === 'undefined') { + return null; + } + + try { + return window.localStorage; + } catch { + return null; + } + } + get length(): number { - return isPlatformBrowser(this.platformId) ? localStorage.length : 0; + return this.storage?.length || 0; } clear(): void { - if (isPlatformBrowser(this.platformId)) { - localStorage.clear(); - } + this.storage?.clear(); } getItem(key: string): string | null { - if (!isPlatformBrowser(this.platformId)) { - return null; - } - return localStorage.getItem(key); + return this.storage?.getItem(key) || null; } key(index: number): string | null { - if (!isPlatformBrowser(this.platformId)) { - return null; - } - return localStorage.key(index); + return this.storage?.key(index) || null; } removeItem(key: string): void { - if (isPlatformBrowser(this.platformId)) { - localStorage.removeItem(key); - } + this.storage?.removeItem(key); } setItem(key: string, value: string): void { - if (isPlatformBrowser(this.platformId)) { - localStorage.setItem(key, value); - } + this.storage?.setItem(key, value); } } From f291993d3516f2c347766c16b82139660c93684a Mon Sep 17 00:00:00 2001 From: sumeyye Date: Mon, 29 Jun 2026 17:15:43 +0300 Subject: [PATCH 3/8] fix: UI tests in the core package relating the ng v22 upgrade --- .../src/lib/tests/autofocus.directive.spec.ts | 30 +-- .../src/lib/tests/capsLock.directive.spec.ts | 52 ++--- .../src/lib/tests/debounce.directive.spec.ts | 31 ++- .../src/lib/tests/environment-utils.spec.ts | 6 +- .../core/src/lib/tests/factory-utils.spec.ts | 3 +- .../core/src/lib/tests/for.directive.spec.ts | 181 ++++++++---------- .../lib/tests/form-submit.directive.spec.ts | 21 +- .../lib/tests/local-storage.service.spec.ts | 34 ++-- .../lib/tests/permission.directive.spec.ts | 15 +- .../src/lib/tests/permission.guard.spec.ts | 68 +++---- .../replaceable-template.directive.spec.ts | 44 +++-- .../lib/tests/show-password-directive.spec.ts | 40 ++-- .../tests/stop-propagation.directive.spec.ts | 9 +- 13 files changed, 301 insertions(+), 233 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 5f46e900bd..6a8d7bf4bd 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 } from 'rxjs'; +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; describe('AutofocusDirective', () => { let spectator: SpectatorDirective; @@ -11,26 +11,32 @@ describe('AutofocusDirective', () => { }); beforeEach(() => { - spectator = createDirective('', { - hostProps: {}, - }); + vi.useFakeTimers(); + + spectator = createDirective(''); directive = spectator.directive; input = spectator.query('input'); }); + afterEach(() => { + if (vi.isFakeTimers()) { + vi.runOnlyPendingTimers(); + } + vi.useRealTimers(); + }); + test('should be created', () => { expect(directive).toBeTruthy(); }); - test('should have 10ms delay', () => { - expect(directive.delay).toBe(10); + test('should have 0ms delay', () => { + expect(directive.delay()).toBe(0); }); - test('should focus element after given delay', () => { - timer(0).subscribe(() => expect('input').not.toBeFocused()); - timer(11).subscribe(() => { - expect('input').toBeFocused(); - expect.hasAssertions(); - }); + test('should focus element after default delay', () => { + expect(input).not.toBeFocused(); + + vi.runOnlyPendingTimers(); + expect(input).toBeFocused(); }); }); 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 e94a099d19..dad3723500 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 @@ -4,7 +4,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TrackCapsLockDirective } from '../directives'; @Component({ - template: ` `, + template: ` `, imports: [TrackCapsLockDirective], }) class TestComponent { @@ -14,6 +14,20 @@ class TestComponent { describe('TrackCapsLockDirective', () => { let fixture: ComponentFixture; let des: DebugElement[]; + let directive: TrackCapsLockDirective; + let emitSpy: ReturnType; + + const createCapsLockEvent = (eventName: string, capsLock: boolean) => { + const event = new KeyboardEvent(eventName, { + key: 'CapsLock', + }); + + Object.defineProperty(event, 'getModifierState', { + value: (key: string) => key === 'CapsLock' && capsLock, + }); + + return event; + }; beforeEach(() => { fixture = TestBed.configureTestingModule({ @@ -23,35 +37,29 @@ describe('TrackCapsLockDirective', () => { fixture.detectChanges(); des = fixture.debugElement.queryAll(By.directive(TrackCapsLockDirective)); + directive = des[0].injector.get(TrackCapsLockDirective); + emitSpy = vi.spyOn(directive.capsLock, 'emit'); + }); + + afterEach(() => { + vi.restoreAllMocks(); }); test.each(['keydown', 'keyup'])( 'is %p works when press capslock and is emit status', eventName => { - const event = new KeyboardEvent(eventName, { - key: 'CapsLock', - modifierCapsLock: true, - }); - window.dispatchEvent(event); - fixture.detectChanges(); - expect(fixture.componentInstance.capsLock).toBe(true); + const event = createCapsLockEvent(eventName, true); + eventName === 'keydown' ? directive.onKeyDown(event) : directive.onKeyUp(event); + expect(emitSpy).toHaveBeenCalledWith(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); + const trueEvent = createCapsLockEvent(eventName, true); + eventName === 'keydown' ? directive.onKeyDown(trueEvent) : directive.onKeyUp(trueEvent); + expect(emitSpy).toHaveBeenCalledWith(true); + const falseEvent = createCapsLockEvent(eventName, false); + eventName === 'keydown' ? directive.onKeyDown(falseEvent) : directive.onKeyUp(falseEvent); + expect(emitSpy).toHaveBeenLastCalledWith(false); }); }); 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 d952965486..ee36dac0ae 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 { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; describe('InputEventDebounceDirective', () => { let spectator: SpectatorDirective; @@ -13,7 +13,9 @@ describe('InputEventDebounceDirective', () => { }); beforeEach(() => { - spectator = createDirective('', { + vi.useFakeTimers(); + + spectator = createDirective('', { hostProps: { inputEventFn }, }); directive = spectator.directive; @@ -21,18 +23,31 @@ describe('InputEventDebounceDirective', () => { inputEventFn.mockClear(); }); + afterEach(() => { + if (vi.isFakeTimers()) { + vi.runOnlyPendingTimers(); + } + vi.useRealTimers(); + }); + test('should be created', () => { expect(directive).toBeTruthy(); }); - test('should have 20ms debounce time', () => { - expect(directive.debounce).toBe(20); + test('should have 300ms debounce time', () => { + expect(directive.debounce()).toBe(300); }); - test('should call fromEvent with target element and target event', async () => { + test('should call fromEvent with target element and target event', () => { + const emitSpy = vi.spyOn(directive.debounceEvent, 'emit'); + spectator.dispatchFakeEvent('input', 'input', true); - timer(0).subscribe(() => expect(inputEventFn).not.toHaveBeenCalled()); - await firstValueFrom(timer(21)); - expect(inputEventFn).toHaveBeenCalled(); + expect(emitSpy).not.toHaveBeenCalled(); + + vi.advanceTimersByTime(299); + expect(emitSpy).not.toHaveBeenCalled(); + + vi.advanceTimersByTime(1); + expect(emitSpy).toHaveBeenCalled(); }); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/environment-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/environment-utils.spec.ts index 353f1ebe69..5dfb3252e5 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/environment-utils.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/environment-utils.spec.ts @@ -97,11 +97,11 @@ describe('EnvironmentUtils', () => { it('should handle request error gracefully and use local environment', async () => { const injector = spectator.inject(Injector); - const injectorSpy = jest.spyOn(injector, 'get'); + const injectorSpy = vi.spyOn(injector, 'get'); const http = spectator.inject(HttpClient); - const requestSpy = jest.spyOn(http, 'request'); + const requestSpy = vi.spyOn(http, 'request'); const environmentService = spectator.inject(EnvironmentService); - const setStateSpy = jest.spyOn(environmentService, 'setState'); + const setStateSpy = vi.spyOn(environmentService, 'setState'); injectorSpy.mockReturnValueOnce(environmentService); injectorSpy.mockReturnValueOnce(http); diff --git a/npm/ng-packs/packages/core/src/lib/tests/factory-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/factory-utils.spec.ts index 06e8a8d044..d72eabe14d 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/factory-utils.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/factory-utils.spec.ts @@ -28,11 +28,12 @@ describe('LazyModuleFactory', () => { const injector = TestBed.inject(Injector); const moduleRef = factory.create(injector); - expect('componentFactoryResolver' in moduleRef).toBe(true); expect('destroy' in moduleRef).toBe(true); expect('injector' in moduleRef).toBe(true); expect('instance' in moduleRef).toBe(true); expect('onDestroy' in moduleRef).toBe(true); + expect(moduleRef.instance).toBeInstanceOf(Module); + expect(moduleRef.injector.get('foo')).toBe('bar'); }); }); }); 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 90c2dc68b0..156d6d20c9 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 @@ -1,6 +1,12 @@ +import { TemplateRef, ɵSIGNAL as SIGNAL } from '@angular/core'; import { SpectatorDirective, createDirectiveFactory } from '@ngneat/spectator/vitest'; import { ForDirective } from '../directives/for.directive'; +const setInputSignal = (inputSignal: () => T, value: T) => { + const node = inputSignal[SIGNAL]; + node.applyValueToInputSignal(node, value); +}; + describe('ForDirective', () => { let spectator: SpectatorDirective; let directive: ForDirective; @@ -9,12 +15,44 @@ describe('ForDirective', () => { directive: ForDirective, }); + const renderChanges = () => { + directive.ngOnChanges(); + spectator.fixture.detectChanges(false); + spectator.fixture.detectChanges(false); + }; + + const resetProjection = () => { + directive['vcRef'].clear(); + directive['lastItemsRef'] = null; + directive['differ'] = null; + }; + + const setDirectiveInputs = (inputs: Partial>) => { + Object.entries(inputs).forEach(([key, value]) => { + setInputSignal(directive[key], value); + }); + resetProjection(); + renderChanges(); + }; + + const getTexts = () => spectator.queryAll('li').map(el => el.textContent.trim()); + + const getTemplateRef = node => { + try { + return node.injector.get(TemplateRef); + } catch { + return null; + } + }; + + const getTemplateRefs = () => + spectator.fixture.debugElement.queryAllNodes(getTemplateRef).map(getTemplateRef); + describe('basic', () => { beforeEach(() => { - spectator = createDirective('
  • {{ item }}
', { - hostProps: { items }, - }); + spectator = createDirective('
  • {{ item }}
'); directive = spectator.directive; + setDirectiveInputs({ items }); }); test('should be created', () => { @@ -29,166 +67,116 @@ describe('ForDirective', () => { }); test('should sync the DOM when change items', () => { - directive.items = [10, 11, 12]; - directive['vcRef'].clear(); - directive['lastItemsRef'] = null; - directive['differ'] = null; - directive.ngOnChanges(); - spectator.detectChanges(); - const elements = spectator.queryAll('li'); + setDirectiveInputs({ items: [10, 11, 12] }); + const elements = spectator.queryAll('li'); expect(elements[1]).toHaveText('11'); expect(elements).toHaveLength(3); }); test('should sync the DOM when add an item', () => { - directive.items = [...items, 6]; - directive['vcRef'].clear(); - directive['lastItemsRef'] = null; - directive['differ'] = null; - directive.ngOnChanges(); - spectator.detectChanges(); - const elements = spectator.queryAll('li'); + setDirectiveInputs({ items: [...items, 6] }); + const elements = spectator.queryAll('li'); expect(elements[6]).toHaveText('6'); expect(elements).toHaveLength(7); }); }); describe('trackBy', () => { - const trackByFn = (_, item) => item; + const trackByFn = (_: number, item: number) => item; + beforeEach(() => { - spectator = createDirective( - '
  • {{ item }}
', - { - hostProps: { items, trackByFn }, - }, - ); + spectator = createDirective('
  • {{ item }}
'); directive = spectator.directive; + setDirectiveInputs({ items, trackBy: trackByFn }); }); test('should be setted the trackBy', () => { - expect(directive.trackBy).toEqual(trackByFn); + expect(directive.trackBy()).toEqual(trackByFn); }); }); describe('with basic order', () => { beforeEach(() => { - spectator = createDirective( - `
    -
  • - {{ item }} -
  • -
`, - ); + spectator = createDirective('
  • {{ item }}
'); directive = spectator.directive; + setDirectiveInputs({ items: [3, 6, 2], orderDir: 'ASC' }); }); test('should order by asc', () => { - const elements = spectator.queryAll('li'); - expect(elements.map(el => el.textContent.trim())).toEqual(['2', '3', '6']); + expect(getTexts()).toEqual(['2', '3', '6']); }); }); describe('with order', () => { beforeEach(() => { spectator = createDirective( - `
    -
  • - {{ item.value }} -
  • -
`, - { - hostProps: { orderDir: 'ASC' }, - }, + '
  • {{ item.value }}
', ); directive = spectator.directive; + setDirectiveInputs({ + items: [{ value: 3 }, { value: 6 }, { value: 2 }], + orderBy: 'value', + orderDir: 'ASC', + }); }); test('should order by asc', () => { - const elements = spectator.queryAll('li'); - expect(elements.map(el => el.textContent.trim())).toEqual(['2', '3', '6']); + expect(getTexts()).toEqual(['2', '3', '6']); }); test('should order by desc', () => { - directive.orderDir = 'DESC'; - directive['vcRef'].clear(); - directive['lastItemsRef'] = null; - directive['differ'] = null; - directive.ngOnChanges(); - spectator.detectChanges(); + setDirectiveInputs({ orderDir: 'DESC' }); - const elements = spectator.queryAll('li'); - expect(elements.map(el => el.textContent.trim())).toEqual(['6', '3', '2']); + expect(getTexts()).toEqual(['6', '3', '2']); }); }); describe('with filter', () => { beforeEach(() => { spectator = createDirective( - `
    -
  • - {{ item.value }} -
  • -
`, - { - hostProps: { filterVal: '' }, - }, + '
  • {{ item.value }}
', ); directive = spectator.directive; + setDirectiveInputs({ + items: [{ value: 'test' }, { value: 'abp' }, { value: 'volo' }], + filterBy: 'value', + filterVal: '', + }); }); test('should not filter when filterVal is empty,', () => { - const elements = spectator.queryAll('li'); - expect(elements.map(el => el.textContent.trim())).toEqual(['test', 'abp', 'volo']); + expect(getTexts()).toEqual(['test', 'abp', 'volo']); }); test('should be filtered', () => { - directive.filterVal = 'volo'; - directive['vcRef'].clear(); - directive['lastItemsRef'] = null; - directive['differ'] = null; - directive.ngOnChanges(); - spectator.detectChanges(); + setDirectiveInputs({ filterVal: 'volo' }); expect(spectator.query('li')).toHaveText('volo'); }); test('should not show an element when filter value not match to any text', () => { - directive.filterVal = 'volos'; - directive.ngOnChanges(); - spectator.detectChanges(); + setDirectiveInputs({ filterVal: 'volos' }); - const elements = spectator.queryAll('li'); - expect(elements).toHaveLength(0); + expect(spectator.queryAll('li')).toHaveLength(0); }); }); describe('with empty ref', () => { beforeEach(() => { - spectator = createDirective( - `
    -
  • - {{ item.value }} -
  • + spectator = createDirective(` +
      + +
    • {{ item.value }}
    • +
      No records found -
    `, - { - hostProps: { items: [] }, - }, - ); +
+ `); directive = spectator.directive; + const [, emptyRef] = getTemplateRefs(); + setDirectiveInputs({ items: [], emptyRef }); }); test('should display the empty ref', () => { @@ -200,12 +188,7 @@ describe('ForDirective', () => { expect(spectator.query('ul')).toHaveText('No records found'); expect(spectator.queryAll('li')).toHaveLength(0); - directive.items = [0]; - directive['vcRef'].clear(); - directive['lastItemsRef'] = null; - directive['differ'] = null; - directive.ngOnChanges(); - spectator.detectChanges(); + setDirectiveInputs({ items: [{ value: 0 }] }); expect(spectator.query('ul')).not.toHaveText('No records found'); expect(spectator.queryAll('li')).toHaveLength(1); 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 8eb3f59bb6..6e7d40f625 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 @@ -2,6 +2,8 @@ import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/vi import { FormSubmitDirective } from '../directives/form-submit.directive'; import { FormsModule, ReactiveFormsModule, FormGroup } from '@angular/forms'; import { timer, firstValueFrom } from 'rxjs'; +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; + describe('FormSubmitDirective', () => { let spectator: SpectatorDirective; @@ -16,6 +18,8 @@ describe('FormSubmitDirective', () => { }); beforeEach(() => { + vi.useFakeTimers(); + spectator = createDirective( '
form content
', { @@ -28,12 +32,19 @@ describe('FormSubmitDirective', () => { directive = spectator.directive; }); + afterEach(() => { + if (vi.isFakeTimers()) { + vi.runOnlyPendingTimers(); + } + vi.useRealTimers(); + }); + test('should be created', () => { expect(directive).toBeTruthy(); }); test('should have 20ms debounce time', () => { - expect(directive.debounce).toBe(20); + expect(directive.debounce()).toBe(200); }); test('should dispatch submit event on keyup event triggered after given debounce time', async () => { @@ -44,8 +55,12 @@ describe('FormSubmitDirective', () => { cancelable: true, }); form?.dispatchEvent(event); - timer(0).subscribe(() => expect(submitEventFn).not.toHaveBeenCalled()); - await firstValueFrom(timer(directive.debounce + 10)); + expect(submitEventFn).not.toHaveBeenCalled(); + + vi.advanceTimersByTime(199); + expect(submitEventFn).not.toHaveBeenCalled(); + + vi.advanceTimersByTime(1); expect(submitEventFn).toHaveBeenCalled(); }); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/local-storage.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/local-storage.service.spec.ts index 8cfd8853a8..3daa086b09 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/local-storage.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/local-storage.service.spec.ts @@ -4,49 +4,59 @@ import { AbpLocalStorageService } from '../services/local-storage.service'; describe('LocalStorageService', () => { let service: AbpLocalStorageService; + let localStorageMock: Storage; beforeEach(() => { + localStorageMock = { + clear: vi.fn(), + getItem: vi.fn(), + key: vi.fn(), + removeItem: vi.fn(), + setItem: vi.fn(), + length: 0, + }; + + vi.stubGlobal('localStorage', localStorageMock); TestBed.configureTestingModule({}); service = TestBed.inject(AbpLocalStorageService); }); + afterEach(() => { + vi.unstubAllGlobals(); + }); + it('should be created', () => { expect(service).toBeTruthy(); }); it('should be called getItem', () => { - const spy = vi.spyOn(service, 'getItem'); service.getItem('test'); - expect(spy).toHaveBeenCalled(); + expect(localStorageMock.getItem).toHaveBeenCalledWith('test'); }); it('should be called setItem', () => { - const spy = vi.spyOn(service, 'setItem'); service.setItem('test', 'value'); - expect(spy).toHaveBeenCalled(); + expect(localStorageMock.setItem).toHaveBeenCalledWith('test', 'value'); }); it('should be called removeItem', () => { - const spy = vi.spyOn(service, 'removeItem'); service.removeItem('test'); - expect(spy).toHaveBeenCalled(); + expect(localStorageMock.removeItem).toHaveBeenCalledWith('test'); }); it('should be called clear', () => { - const spy = vi.spyOn(service, 'clear'); service.clear(); - expect(spy).toHaveBeenCalled(); + expect(localStorageMock.clear).toHaveBeenCalled(); }); it('should be called key', () => { - const spy = vi.spyOn(service, 'key'); service.key(0); - expect(spy).toHaveBeenCalled(); + expect(localStorageMock.key).toHaveBeenCalledWith(0); }); it('should be called length', () => { - const spy = vi.spyOn(service, 'length', 'get'); + vi.stubGlobal('localStorage', { ...localStorageMock, length: 1 }); service.length; - expect(spy).toHaveBeenCalled(); + expect(service.length).toBe(1); }); }); 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 9d5a9c586d..fd79d365eb 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,10 +1,15 @@ -import { ChangeDetectorRef } from '@angular/core'; +import { ChangeDetectorRef, ɵSIGNAL as SIGNAL } 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 { QUEUE_MANAGER } from '../tokens/queue.token'; +const setInputSignal = (inputSignal: () => T, value: T) => { + const node = inputSignal[SIGNAL]; + node.applyValueToInputSignal(node, value); +}; + describe('PermissionDirective', () => { let spectator: SpectatorDirective; let directive: PermissionDirective; @@ -44,19 +49,19 @@ describe('PermissionDirective', () => { it('should handle permission input', () => { grantedPolicy$.next(false); - directive.condition = 'new-permission'; + setInputSignal(directive.condition, 'new-permission'); directive.ngOnChanges(); grantedPolicy$.next(true); expect(directive).toBeTruthy(); - expect(directive.condition).toBe('new-permission'); + expect(directive.condition()).toBe('new-permission'); }); it('should handle runChangeDetection input', () => { grantedPolicy$.next(false); - directive.runChangeDetection = true; + setInputSignal(directive.runChangeDetection, true); directive.ngOnChanges(); grantedPolicy$.next(true); expect(directive).toBeTruthy(); - expect(directive.runChangeDetection).toBe(true); + 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 6486a7bf41..a171178170 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 @@ -1,5 +1,3 @@ -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'; @@ -9,8 +7,8 @@ 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 { AuthService } from '../abstracts'; +import { ConfigStateService, RouteBasedCultureUrlService, RoutesService } from '../services'; @Component({ template: '' }) class DummyComponent {} @@ -20,8 +18,11 @@ class DummyComponent {} describe('authGuard', () => { let permissionService: SpyObject; let httpErrorReporter: SpyObject; + let routesService: Pick; + let routeCultureUrl: Pick; + let configStateService: Pick; - const mockOAuthService = { + const authService = { isAuthenticated: true, }; @@ -53,46 +54,30 @@ describe('authGuard', () => { beforeEach(() => { httpErrorReporter = createSpyObject(HttpErrorReporterService); permissionService = createSpyObject(PermissionService); + permissionService.getGrantedPolicy$.andReturn(of(true)); + routesService = { + find: vi.fn(), + }; + routeCultureUrl = { + getRoutePathForMatching: vi.fn((_: Router, url: string) => url), + }; + configStateService = { + getAll$: vi.fn(() => of({ auth: { grantedPolicies: {} } })), + }; TestBed.configureTestingModule({ providers: [ - provideHttpClient(), - provideHttpClientTesting(), - { provide: AuthService, useValue: mockOAuthService }, + { provide: AuthService, useValue: authService }, { provide: PermissionService, useValue: permissionService }, { provide: HttpErrorReporterService, useValue: httpErrorReporter }, + { provide: RoutesService, useValue: routesService }, + { provide: RouteBasedCultureUrlService, useValue: routeCultureUrl }, + { provide: ConfigStateService, useValue: configStateService }, provideRouter(routes), - provideAbpCore( - withOptions({ - environment: { - apis: { - default: { - url: 'http://localhost:4200', - }, - }, - application: { - baseUrl: 'http://localhost:4200', - name: 'TestApp', - }, - remoteEnv: { - url: 'http://localhost:4200', - mergeStrategy: 'deepmerge', - }, - }, - 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'); @@ -103,17 +88,24 @@ describe('authGuard', () => { it('should return false and report an error when the grantedPolicy is false', () => { permissionService.getGrantedPolicy$.andReturn(of(false)); - expect(permissionService.getGrantedPolicy$).toBeDefined(); - expect(httpErrorReporter.reportError).toBeDefined(); + return RouterTestingHarness.create('/dummy').then(() => { + expect(TestBed.inject(Router).url).toEqual('/'); + expect(httpErrorReporter.reportError).toHaveBeenCalledWith({ status: 403 }); + }); }); it('should check the requiredPolicy from RoutesService', async () => { + routesService.find = vi.fn(predicate => { + const route = { path: '/zibzib', requiredPolicy: 'TestPolicy' }; + return predicate(route) ? route : null; + }); permissionService.getGrantedPolicy$.mockImplementation(policy => { return of(policy === 'TestPolicy'); }); - await RouterTestingHarness.create('/dummy'); + await RouterTestingHarness.create('/zibzib'); - expect(TestBed.inject(Router).url).toEqual('/dummy'); + expect(permissionService.getGrantedPolicy$).toHaveBeenCalledWith('TestPolicy'); + expect(TestBed.inject(Router).url).toEqual('/zibzib'); expect(httpErrorReporter.reportError).not.toHaveBeenCalled(); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/replaceable-template.directive.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/replaceable-template.directive.spec.ts index 3cd625cd3c..819ab01936 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/replaceable-template.directive.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/replaceable-template.directive.spec.ts @@ -1,4 +1,4 @@ -import { Component, Input, inject, output } from '@angular/core'; +import { Component, inject, input, output, ɵSIGNAL as SIGNAL } from '@angular/core'; import { Router } from '@angular/router'; import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/vitest'; import { BehaviorSubject } from 'rxjs'; @@ -6,34 +6,37 @@ import { ReplaceableTemplateDirective } from '../directives/replaceable-template import { ReplaceableComponents } from '../models/replaceable-components'; import { ReplaceableComponentsService } from '../services/replaceable-components.service'; +const setInputSignal = (inputSignal: () => T, value: T) => { + const node = inputSignal[SIGNAL]; + node.applyValueToInputSignal(node, value); +}; + @Component({ selector: 'abp-default-component', template: '

default

', - exportAs: 'abpDefaultComponent' + exportAs: 'abpDefaultComponent', }) class DefaultComponent { - @Input() - oneWay; + onOneWay = input(); - @Input() - twoWay: boolean; + twoWay = input(); readonly twoWayChange = output(); readonly someOutput = output(); setTwoWay(value) { - this.twoWay = value; + setInputSignal(this.twoWay, value); this.twoWayChange.emit(value); } } @Component({ selector: 'abp-external-component', - template: '

external

' + template: '

external

', }) class ExternalComponent { - data = inject>('REPLACEABLE_DATA' as any, { optional: true })!; + data = inject>('REPLACEABLE_DATA' as any)!; } describe('ReplaceableTemplateDirective', () => { @@ -54,11 +57,12 @@ describe('ReplaceableTemplateDirective', () => { beforeEach(() => { spectator = createDirective( ` -
+ -
+ `, { + detectChanges: false, hostProps: { oneWay: { label: 'Test' }, twoWay: false, @@ -67,6 +71,15 @@ describe('ReplaceableTemplateDirective', () => { }, }, ); + setInputSignal(spectator.directive.data, { + inputs: { + oneWay: { value: { label: 'Test' } }, + twoWay: { value: false, twoWay: true }, + }, + outputs: { twoWayChange, someOutput }, + componentKey: 'TestModule.TestComponent', + }); + spectator.detectChanges(); }); it('should create directive successfully', () => { @@ -83,6 +96,7 @@ describe('ReplaceableTemplateDirective', () => { `, { + detectChanges: false, hostProps: { oneWay: { label: 'Test' }, twoWay: false, @@ -91,6 +105,14 @@ describe('ReplaceableTemplateDirective', () => { }, }, ); + setInputSignal(spectator.directive.data, { + inputs: { + oneWay: { value: { label: 'Test' } }, + twoWay: { value: false, twoWay: true }, + }, + outputs: { twoWayChange: vi.fn(), someOutput: vi.fn() }, + componentKey: 'TestModule.TestComponent', + }); expect(spectator.directive).toBeTruthy(); }); }); 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 e1266094cf..641dd5a911 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,37 +1,51 @@ -import { Component, DebugElement, ChangeDetectorRef } from '@angular/core'; +import { Component, DebugElement, ɵSIGNAL as SIGNAL } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { ShowPasswordDirective } from '../directives'; @Component({ - template: ` - + template: ` + - `, + `, imports: [ShowPasswordDirective], }) -class TestComponent { - showPassword = false; -} +class TestComponent {} + +const setInputSignal = (inputSignal: () => T, value: T) => { + const node = inputSignal[SIGNAL]; + node.applyValueToInputSignal(node, value); +}; describe('ShowPasswordDirective', () => { let fixture: ComponentFixture; let des: DebugElement[]; let desAll: DebugElement[]; - let bareInput; + + const detectChanges = () => { + fixture.detectChanges(); + TestBed.flushEffects(); + }; + + const setShowPassword = (index: number, value: boolean) => { + setInputSignal(des[index].injector.get(ShowPasswordDirective).abpShowPassword, value); + detectChanges(); + }; beforeEach(() => { fixture = TestBed.configureTestingModule({ imports: [TestComponent], }).createComponent(TestComponent); - fixture.detectChanges(); + detectChanges(); des = fixture.debugElement.queryAll(By.directive(ShowPasswordDirective)); desAll = fixture.debugElement.queryAll(By.all()); - bareInput = fixture.debugElement.query(By.css('input:not([abpShowPassword])')); + setShowPassword(0, true); + setShowPassword(1, false); + setShowPassword(2, false); }); it('should have three input has ShowPasswordDirective elements', () => { @@ -52,11 +66,7 @@ describe('ShowPasswordDirective', () => { 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(); + setShowPassword(2, true); 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 8e0595c3c3..88356b51b2 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 @@ -1,5 +1,6 @@ import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/vitest'; import { StopPropagationDirective } from '../directives/stop-propagation.directive'; +import { beforeEach, describe, expect, test, vi } from 'vitest'; describe('StopPropagationDirective', () => { let spectator: SpectatorDirective; @@ -13,12 +14,13 @@ describe('StopPropagationDirective', () => { beforeEach(() => { spectator = createDirective( - '', + '', { - hostProps: { parentClickEventFn, childClickEventFn }, + hostProps: { parentClickEventFn }, }, ); directive = spectator.directive; + directive.stopPropEvent.subscribe(childClickEventFn); link = spectator.query('a'); childClickEventFn.mockClear(); parentClickEventFn.mockClear(); @@ -29,8 +31,7 @@ describe('StopPropagationDirective', () => { }); test('should not call click event of parent when child element is clicked', () => { - spectator.setHostInput({ parentClickEventFn, childClickEventFn }); - spectator.click('a'); + spectator.click(link); spectator.detectChanges(); expect(childClickEventFn).toHaveBeenCalled(); expect(parentClickEventFn).not.toHaveBeenCalled(); From 48f7fc422efc3e772f9870d2db3d337e2cd68f94 Mon Sep 17 00:00:00 2001 From: sumeyye Date: Tue, 30 Jun 2026 13:17:57 +0300 Subject: [PATCH 4/8] fix: some elements for the failing tests --- .../src/lib/components/button/button.component.ts | 8 ++++---- .../src/lib/components/loader-bar/loader-bar.component.ts | 4 ++-- .../theme-shared/src/lib/directives/loading.directive.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/button/button.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/button/button.component.ts index 4462e77c45..4a29cb4f0b 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/button/button.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/button/button.component.ts @@ -63,10 +63,6 @@ export class ButtonComponent implements OnInit { this.isLoading() ? 'fa fa-spinner fa-spin' : this.iconClass() || 'd-none', ); - setLoading(value: boolean): void { - this.modalLoading.set(value); - } - ngOnInit() { const attributes = this.attributes(); if (attributes) { @@ -77,4 +73,8 @@ export class ButtonComponent implements OnInit { }); } } + + setLoading(value: boolean): void { + this.modalLoading.set(value); + } } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts index cb409e03de..7530b6cb5a 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts @@ -40,8 +40,8 @@ export class LoaderBarComponent implements OnDestroy, OnInit { readonly containerClass = input('abp-loader-bar'); readonly color = input('#77b6ff'); - protected readonly isLoading = signal(false); - protected readonly progressLevel = signal(0); + readonly isLoading = signal(false); + readonly progressLevel = signal(0); interval = new Subscription(); timer = new Subscription(); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/directives/loading.directive.ts b/npm/ng-packs/packages/theme-shared/src/lib/directives/loading.directive.ts index 3c5c01c9f9..937b3a0ca1 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/directives/loading.directive.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/directives/loading.directive.ts @@ -32,7 +32,7 @@ export class LoadingDirective implements OnInit, OnDestroy { readonly targetElementInput = input(undefined, { alias: 'abpLoadingTargetElement' }); readonly delay = input(0, { alias: 'abpLoadingDelay' }); - private targetElement: HTMLElement | undefined; + targetElement: HTMLElement | undefined; componentRef: ComponentRef | null = null; rootNode: HTMLDivElement | null = null; From 9ad342b41424868f856928193a595b8977c7783c Mon Sep 17 00:00:00 2001 From: sumeyye Date: Tue, 30 Jun 2026 13:18:09 +0300 Subject: [PATCH 5/8] add: a signal util --- .../packages/theme-shared/src/lib/tests/utils/index.ts | 1 + .../theme-shared/src/lib/tests/utils/input-signal.ts | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 npm/ng-packs/packages/theme-shared/src/lib/tests/utils/input-signal.ts diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/utils/index.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/utils/index.ts index 73b11724ec..1de5c7f22c 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/utils/index.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/utils/index.ts @@ -1 +1,2 @@ +export * from './input-signal'; export * from './setup-component-resources'; \ No newline at end of file diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/utils/input-signal.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/utils/input-signal.ts new file mode 100644 index 0000000000..a985285d7c --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/utils/input-signal.ts @@ -0,0 +1,6 @@ +import { ɵSIGNAL as SIGNAL } from '@angular/core'; + +export const setInputSignal = (inputSignal: () => T, value: T) => { + const node = inputSignal[SIGNAL]; + node.applyValueToInputSignal(node, value); +}; \ No newline at end of file From c4c59f11e67d8a32ddf52a1c09d64f9449371388 Mon Sep 17 00:00:00 2001 From: sumeyye Date: Tue, 30 Jun 2026 13:18:42 +0300 Subject: [PATCH 6/8] fix: UI tests in the theme shared package relating the ng v22 upgrade --- .../src/lib/tests/button.component.spec.ts | 32 +++++++++---------- .../src/lib/tests/card-body.component.spec.ts | 22 +++++++------ .../lib/tests/card-footer.component.spec.ts | 24 ++++++++------ .../lib/tests/card-header.component.spec.ts | 22 +++++++------ .../lib/tests/card-header.directive.spec.ts | 11 +++---- .../src/lib/tests/checkbox.component.spec.ts | 18 +++++++---- .../src/lib/tests/ellipsis.directive.spec.ts | 27 +++++++++++----- .../lib/tests/form-input.component.spec.ts | 20 ++++++------ .../lib/tests/loader-bar.component.spec.ts | 22 ++++++------- .../src/lib/tests/loading.directive.spec.ts | 21 +++++++----- .../src/lib/tests/toaster.service.spec.ts | 7 ++-- 11 files changed, 130 insertions(+), 96 deletions(-) diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/button.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/button.component.spec.ts index 73ae89d96d..d0dbdb996d 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/button.component.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/button.component.spec.ts @@ -1,19 +1,25 @@ +import { ɵSIGNAL as SIGNAL } from '@angular/core'; import { createHostFactory, SpectatorHost } from '@ngneat/spectator/vitest'; import { ButtonComponent } from '../components'; +const setInputSignal = (inputSignal: () => T, value: T) => { + const node = inputSignal[SIGNAL]; + node.applyValueToInputSignal(node, value); +}; + describe('ButtonComponent', () => { let spectator: SpectatorHost; const createHost = createHostFactory(ButtonComponent); beforeEach( - () => - (spectator = createHost( - 'Button', - { - hostProps: { attributes: { autofocus: '', name: 'abp-button' } }, - }, - )), + () => { + spectator = createHost('Button', { + detectChanges: false, + }); + setInputSignal(spectator.component.iconClass, 'fa fa-check'); + spectator.detectChanges(); + }, ); it('should display the button', () => { @@ -48,23 +54,17 @@ describe('ButtonComponent', () => { }); it('should display the spinner icon when loading input is true', () => { - spectator = createHost( - 'Button', - { hostProps: { loading: true } }, - ); + setInputSignal(spectator.component.loading, true); spectator.detectComponentChanges(); expect(spectator.query('i')).toHaveClass('fa-spinner'); }); it('should clear the spinner icon when loading input becomes false', () => { - spectator = createHost( - 'Button', - { hostProps: { loading: true } }, - ); + setInputSignal(spectator.component.loading, true); spectator.detectComponentChanges(); expect(spectator.query('i')).toHaveClass('fa-spinner'); - spectator.setHostInput({ loading: false }); + setInputSignal(spectator.component.loading, false); spectator.detectComponentChanges(); expect(spectator.query('i')).toHaveClass('fa-check'); expect(spectator.query('i')).not.toHaveClass('fa-spinner'); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/card-body.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/card-body.component.spec.ts index 83cc302625..efe1d6fc04 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/card-body.component.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/card-body.component.spec.ts @@ -1,5 +1,6 @@ import { createHostFactory, SpectatorHost } from '@ngneat/spectator'; import { CardBodyComponent } from '../components'; +import { setInputSignal } from './utils'; describe('AbpCardBodyComponent', () => { let spectator: SpectatorHost; @@ -8,16 +9,17 @@ describe('AbpCardBodyComponent', () => { beforeEach( () => - (spectator = createHost( - ` -

Body

-
`, - { - hostProps: { attributes: { autofocus: '', name: 'abp-card-body' } }, - }, - )), + { + spectator = createHost( + `

Body

`, + { + detectChanges: false, + hostProps: { attributes: { autofocus: '', name: 'abp-card-body' } }, + }, + ); + setInputSignal(spectator.component.cardBodyStyle, 'background-color: red;'); + spectator.detectChanges(); + }, ); it('should create an instance', () => { diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/card-footer.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/card-footer.component.spec.ts index 2b98479b9f..2e7074ce58 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/card-footer.component.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/card-footer.component.spec.ts @@ -1,5 +1,6 @@ import { createHostFactory, SpectatorHost } from '@ngneat/spectator'; import { CardFooterComponent } from '../components'; +import { setInputSignal } from './utils'; describe('AbpCardFooterComponent', () => { let spectator: SpectatorHost; @@ -8,16 +9,19 @@ describe('AbpCardFooterComponent', () => { beforeEach( () => - (spectator = createHost( - ` -

Footer

-
`, - { - hostProps: { attributes: { autofocus: '', name: 'abp-card-footer' } }, - }, - )), + { + spectator = createHost( + ` +

Footer

+
`, + { + detectChanges: false, + hostProps: { attributes: { autofocus: '', name: 'abp-card-footer' } }, + }, + ); + setInputSignal(spectator.component.cardFooterStyle, 'background-color: red;'); + spectator.detectChanges(); + }, ); it('should create an instance', () => { diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/card-header.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/card-header.component.spec.ts index 279cfe90bf..1c75c588ce 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/card-header.component.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/card-header.component.spec.ts @@ -1,5 +1,6 @@ import { createHostFactory, SpectatorHost } from '@ngneat/spectator'; import { CardHeaderComponent } from '../components'; +import { setInputSignal } from './utils'; describe('AbpCardHeaderComponent', () => { let spectator: SpectatorHost; @@ -7,16 +8,17 @@ describe('AbpCardHeaderComponent', () => { beforeEach( () => - (spectator = createHost( - ` - Header - `, - { - hostProps: { attributes: { autofocus: '', name: 'abp-card-header' } }, - }, - )), + { + spectator = createHost( + ` Header `, + { + detectChanges: false, + hostProps: { attributes: { autofocus: '', name: 'abp-card-header' } }, + }, + ); + setInputSignal(spectator.component.cardHeaderStyle, 'background-color: red;'); + spectator.detectChanges(); + }, ); it('should create an instance', () => { diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/card-header.directive.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/card-header.directive.spec.ts index 122de05ed8..08a99323e6 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/card-header.directive.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/card-header.directive.spec.ts @@ -7,15 +7,14 @@ describe('AbpCardHeaderDirective', () => { const createHost = createHostFactory(CardHeaderDirective); beforeEach( - () => - (spectator = createHost( - `
-
`, + () => { + spectator = createHost( + `
`, { hostProps: { attributes: { autofocus: '', name: 'abp-card-header' } }, }, - )), + ); + }, ); it('should create an instance', () => { diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/checkbox.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/checkbox.component.spec.ts index 4ea0946c01..e983eb7bbb 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/checkbox.component.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/checkbox.component.spec.ts @@ -1,5 +1,6 @@ import { createHostFactory, SpectatorHost } from '@ngneat/spectator/vitest'; import { FormCheckboxComponent } from '../components/checkbox/checkbox.component'; +import { setInputSignal } from './utils'; describe('FormCheckboxComponent', () => { let spectator: SpectatorHost; @@ -7,13 +8,18 @@ describe('FormCheckboxComponent', () => { const createHost = createHostFactory(FormCheckboxComponent); beforeEach( - () => - (spectator = createHost( - '', + () => { + spectator = createHost( + '', { + detectChanges: false, hostProps: { attributes: { autofocus: '', name: 'abp-checkbox' } }, }, - )), + ); + setInputSignal(spectator.component.checkboxId, 'checkbox-id'); + setInputSignal(spectator.component.checkboxReadonly, true); + spectator.detectChanges(); + }, ); it('should display the input', () => { @@ -29,13 +35,13 @@ describe('FormCheckboxComponent', () => { }); it('should be readonly when checkboxReadonly is true', () => { - spectator.component.checkboxReadonly = true; + setInputSignal(spectator.component.checkboxReadonly, true); spectator.detectComponentChanges(); expect(spectator.query('[readonly]')).toBeTruthy(); }); it('should not contain readonly when checboxReadonly is false', () => { - spectator.component.checkboxReadonly = false; + setInputSignal(spectator.component.checkboxReadonly, false); spectator.detectComponentChanges(); expect(spectator.query('[disabled]')).toBeFalsy(); }); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/ellipsis.directive.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/ellipsis.directive.spec.ts index e9b0e45a0d..438675fb8c 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/ellipsis.directive.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/ellipsis.directive.spec.ts @@ -1,5 +1,6 @@ import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/vitest'; import { EllipsisDirective } from '../directives/ellipsis.directive'; +import { setInputSignal } from './utils'; describe('EllipsisDirective', () => { let spectator: SpectatorDirective; @@ -13,12 +14,13 @@ describe('EllipsisDirective', () => { spectator = createDirective( '
test content
', { - hostProps: { - title: 'test title', - width: '100px', - }, + detectChanges: false, }, ); + setInputSignal(spectator.directive.width, '100px'); + setInputSignal(spectator.directive.enabled, true); + setInputSignal(spectator.directive.title, 'title'); + spectator.detectChanges(); directive = spectator.directive; el = spectator.query('div'); }); @@ -28,15 +30,15 @@ describe('EllipsisDirective', () => { }); test('should have 100px ellipsis width', () => { - expect(directive.width).toBe('100px'); + expect(directive.width()).toBe('100px'); }); test('should be enabled if abpEllipsisEnabled input is true', () => { - expect(directive.enabled).toBe(true); + expect(directive.enabled()).toBe(true); }); test('should have given title', () => { - expect(directive.title).toBe('test title'); + expect(directive.title()).toBe('title'); }); test('should add abp-ellipsis-inline class to element if width is given', () => { @@ -48,6 +50,7 @@ describe('EllipsisDirective when title is not specified', () => { let spectator: SpectatorDirective; let directive: EllipsisDirective; let el: HTMLDivElement; + const createDirective = createDirectiveFactory({ directive: EllipsisDirective, }); @@ -62,12 +65,16 @@ describe('EllipsisDirective when title is not specified', () => { }, }, ); + setInputSignal(spectator.directive.width, '100px'); + setInputSignal(spectator.directive.enabled, true); + setInputSignal(spectator.directive.title, undefined); + spectator.detectChanges(); directive = spectator.directive; el = spectator.query('div') as HTMLDivElement; }); test('should have element innerText as title', () => { - expect(directive.title).toBe(el.innerText); + expect(directive.title()).toBe(el.innerText); }); }); @@ -89,6 +96,10 @@ describe('EllipsisDirective when width is not given', () => { }, }, ); + setInputSignal(spectator.directive.width, undefined); + setInputSignal(spectator.directive.enabled, true); + setInputSignal(spectator.directive.title, 'test title'); + spectator.detectChanges(); directive = spectator.directive; el = spectator.query('div') as HTMLDivElement; }); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/form-input.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/form-input.component.spec.ts index e6ae275a95..8e35e4c7b7 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/form-input.component.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/form-input.component.spec.ts @@ -1,5 +1,6 @@ import { createHostFactory, SpectatorHost } from '@ngneat/spectator/vitest'; import { FormInputComponent } from '../components/form-input/form-input.component'; +import { setInputSignal } from './utils'; describe('FormInputComponent', () => { @@ -8,13 +9,14 @@ describe('FormInputComponent', () => { const createHost = createHostFactory(FormInputComponent); beforeEach( - () => - (spectator = createHost( - '', - { - hostProps: { attributes: { autofocus: '', name: 'abp-form-input' } }, - }, - )), + () => { + spectator = createHost( + '', + { + hostProps: { attributes: { autofocus: '', name: 'abp-form-input' } }, + }, + ); + }, ); it('should display the input', () => { @@ -30,13 +32,13 @@ describe('FormInputComponent', () => { }); it('should be readonly when inputReadonly is true', () => { - spectator.component.inputReadonly = true; + setInputSignal(spectator.component.inputReadonly, true); spectator.detectComponentChanges(); expect(spectator.query('[readonly]')).toBeTruthy(); }); it('should not contain readonly when inputReadonly is false', () => { - spectator.component.inputReadonly = false; + setInputSignal(spectator.component.inputReadonly, false); spectator.detectComponentChanges(); expect(spectator.query('[disabled]')).toBeFalsy(); }); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/loader-bar.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/loader-bar.component.spec.ts index a825cbf7db..eb2feddddb 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/loader-bar.component.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/loader-bar.component.spec.ts @@ -4,7 +4,8 @@ import { NavigationStart, Router } from '@angular/router'; import { createComponentFactory, Spectator } from '@ngneat/spectator/vitest'; import { combineLatest, firstValueFrom, Subject, timer } from 'rxjs'; import { LoaderBarComponent } from '../components/loader-bar/loader-bar.component'; -import { setupComponentResources } from './utils'; +import { setupComponentResources, setInputSignal } from './utils'; + describe('LoaderBarComponent', () => { let spectator: Spectator; @@ -34,8 +35,8 @@ describe('LoaderBarComponent', () => { }); it('should initial variable values are correct', () => { - expect(spectator.component.containerClass).toBe('abp-loader-bar'); - expect(spectator.component.color).toBe('#77b6ff'); + expect(spectator.component.containerClass()).toBe('abp-loader-bar'); + expect(spectator.component.color()).toBe('#77b6ff'); }); it('should increase the progressLevel', async () => { @@ -45,8 +46,7 @@ describe('LoaderBarComponent', () => { spectator.detectChanges(); await new Promise(resolve => setTimeout(resolve, 10)); - - expect(spectator.component.progressLevel > 0).toBeTruthy(); + expect(spectator.component.progressLevel()).toBeGreaterThan(0); }); @@ -100,15 +100,15 @@ describe('LoaderBarComponent', () => { spectator.detectChanges(); attempts = 0; - while (spectator.component.progressLevel !== 100 && attempts < 50) { + while (spectator.component.progressLevel() !== 100 && attempts < 50) { await new Promise(resolve => setTimeout(resolve, 10)); spectator.detectChanges(); attempts++; } - expect(spectator.component.progressLevel).toBe(100); + expect(spectator.component.progressLevel()).toBe(100); await firstValueFrom(timer(spectator.component.stopDelay + 10)); - expect(spectator.component.progressLevel).toBe(0); + expect(spectator.component.progressLevel()).toBe(0); }); it('should stop the loading with navigation', async () => { @@ -130,15 +130,15 @@ describe('LoaderBarComponent', () => { spectator.detectChanges(); attempts = 0; - while (spectator.component.progressLevel !== 100 && attempts < 50) { + while (spectator.component.progressLevel() !== 100 && attempts < 50) { await new Promise(resolve => setTimeout(resolve, 10)); spectator.detectChanges(); attempts++; } - expect(spectator.component.progressLevel).toBe(100); + expect(spectator.component.progressLevel()).toBe(100); await firstValueFrom(timer(spectator.component.stopDelay + 10)); - expect(spectator.component.progressLevel).toBe(0); + expect(spectator.component.progressLevel()).toBe(0); }); describe('#startLoading', () => { diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/loading.directive.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/loading.directive.spec.ts index f80388d478..638584612f 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/loading.directive.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/loading.directive.spec.ts @@ -1,7 +1,8 @@ +import { Component } from '@angular/core'; import { SpectatorDirective, createDirectiveFactory } from '@ngneat/spectator/vitest'; import { LoadingDirective } from '../directives'; import { LoadingComponent } from '../components'; -import { Component } from '@angular/core'; +import { setInputSignal } from './utils'; @Component({ selector: 'abp-dummy', @@ -30,10 +31,10 @@ describe('LoadingDirective', () => { }); it('should handle loading input', async () => { - spectator.directive.loading = false; + setInputSignal(spectator.directive.loading, false); await new Promise(resolve => setTimeout(resolve, 10)); expect(spectator.directive).toBeTruthy(); - expect(spectator.directive.loading).toBe(false); + expect(spectator.directive.loading()).toBe(false); }); }); @@ -42,11 +43,15 @@ describe('LoadingDirective', () => { beforeEach(() => { spectator = createDirective( - '
Testing Loading Directive
', + '
Testing Loading Directive
', { - hostProps: { loading: true, target: mockTarget, delay: 0 }, + detectChanges: false, }, ); + setInputSignal(spectator.directive.loading, true); + setInputSignal(spectator.directive.delay, 0); + setInputSignal(spectator.directive.targetElementInput, mockTarget); + spectator.detectChanges(); }); it('should create directive with custom target', () => { @@ -55,17 +60,17 @@ describe('LoadingDirective', () => { }); it('should handle delay input', async () => { - spectator.directive.delay = 100; + setInputSignal(spectator.directive.delay, 100); await new Promise(resolve => setTimeout(resolve, 10)); expect(spectator.directive).toBeTruthy(); }); it('should handle loading state changes', async() => { - spectator.directive.loading = false; + setInputSignal(spectator.directive.loading, false); await new Promise(resolve => setTimeout(resolve, 10)); expect(spectator.directive).toBeTruthy(); - spectator.directive.loading = true; + setInputSignal(spectator.directive.loading, true); await new Promise(resolve => setTimeout(resolve, 10)); expect(spectator.directive).toBeTruthy(); }); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/toaster.service.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/toaster.service.spec.ts index 401da50070..0259bbd945 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/toaster.service.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/toaster.service.spec.ts @@ -8,10 +8,13 @@ import { ToasterService } from '../services/toaster.service'; describe('ToasterService', () => { let spectator: SpectatorService; let service: ToasterService; + const mockComponentRef = { changeDetectorRef: { detectChanges: vi.fn() }, - instance: {} as ToastContainerComponent, - } as unknown as ComponentRef; + instance: { + setToasts: vi.fn(), remove: vi.fn(), toasts: [], top: 0, right: 0, bottom: 0, left: 0, + }, + }; const contentProjectionService = { projectContent: vi.fn().mockReturnValue(mockComponentRef), From f521b55431b4f44ebcc5c54784ce8ff32af50d6f Mon Sep 17 00:00:00 2001 From: sumeyye Date: Tue, 30 Jun 2026 13:28:40 +0300 Subject: [PATCH 7/8] update: common input signal for the tests --- .../packages/core/src/lib/tests/for.directive.spec.ts | 8 ++------ .../core/src/lib/tests/permission.directive.spec.ts | 8 ++------ .../src/lib/tests/replaceable-template.directive.spec.ts | 7 +------ .../core/src/lib/tests/show-password-directive.spec.ts | 9 ++------- npm/ng-packs/packages/core/src/lib/tests/utils/index.ts | 4 ++++ .../packages/core/src/lib/tests/utils/input-signal.ts | 6 ++++++ .../theme-shared/src/lib/tests/button.component.spec.ts | 8 ++------ 7 files changed, 19 insertions(+), 31 deletions(-) create mode 100644 npm/ng-packs/packages/core/src/lib/tests/utils/index.ts create mode 100644 npm/ng-packs/packages/core/src/lib/tests/utils/input-signal.ts 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 156d6d20c9..c25ebe9655 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 @@ -1,11 +1,7 @@ -import { TemplateRef, ɵSIGNAL as SIGNAL } from '@angular/core'; +import { TemplateRef } from '@angular/core'; import { SpectatorDirective, createDirectiveFactory } from '@ngneat/spectator/vitest'; import { ForDirective } from '../directives/for.directive'; - -const setInputSignal = (inputSignal: () => T, value: T) => { - const node = inputSignal[SIGNAL]; - node.applyValueToInputSignal(node, value); -}; +import { setInputSignal } from './utils'; describe('ForDirective', () => { let spectator: SpectatorDirective; 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 fd79d365eb..152c2a7b32 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,14 +1,10 @@ -import { ChangeDetectorRef, ɵSIGNAL as SIGNAL } from '@angular/core'; +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 { QUEUE_MANAGER } from '../tokens/queue.token'; - -const setInputSignal = (inputSignal: () => T, value: T) => { - const node = inputSignal[SIGNAL]; - node.applyValueToInputSignal(node, value); -}; +import { setInputSignal } from './utils'; describe('PermissionDirective', () => { let spectator: SpectatorDirective; diff --git a/npm/ng-packs/packages/core/src/lib/tests/replaceable-template.directive.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/replaceable-template.directive.spec.ts index 819ab01936..d31c20ee60 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/replaceable-template.directive.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/replaceable-template.directive.spec.ts @@ -5,12 +5,7 @@ import { BehaviorSubject } from 'rxjs'; import { ReplaceableTemplateDirective } from '../directives/replaceable-template.directive'; import { ReplaceableComponents } from '../models/replaceable-components'; import { ReplaceableComponentsService } from '../services/replaceable-components.service'; - -const setInputSignal = (inputSignal: () => T, value: T) => { - const node = inputSignal[SIGNAL]; - node.applyValueToInputSignal(node, value); -}; - +import { setInputSignal } from './utils'; @Component({ selector: 'abp-default-component', template: '

default

', 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 641dd5a911..3ae33bbe7d 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,8 +1,8 @@ -import { Component, DebugElement, ɵSIGNAL as SIGNAL } from '@angular/core'; +import { Component, DebugElement } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { ShowPasswordDirective } from '../directives'; - +import { setInputSignal } from './utils'; @Component({ template: ` @@ -12,11 +12,6 @@ import { ShowPasswordDirective } from '../directives'; }) class TestComponent {} -const setInputSignal = (inputSignal: () => T, value: T) => { - const node = inputSignal[SIGNAL]; - node.applyValueToInputSignal(node, value); -}; - describe('ShowPasswordDirective', () => { let fixture: ComponentFixture; let des: DebugElement[]; diff --git a/npm/ng-packs/packages/core/src/lib/tests/utils/index.ts b/npm/ng-packs/packages/core/src/lib/tests/utils/index.ts new file mode 100644 index 0000000000..8f0a38145e --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/utils/index.ts @@ -0,0 +1,4 @@ +export * from './input-signal'; +export * from './common.utils'; +export * from './mock-compare-function'; +export * from './permission-service.spec.utils'; \ No newline at end of file diff --git a/npm/ng-packs/packages/core/src/lib/tests/utils/input-signal.ts b/npm/ng-packs/packages/core/src/lib/tests/utils/input-signal.ts new file mode 100644 index 0000000000..a985285d7c --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/utils/input-signal.ts @@ -0,0 +1,6 @@ +import { ɵSIGNAL as SIGNAL } from '@angular/core'; + +export const setInputSignal = (inputSignal: () => T, value: T) => { + const node = inputSignal[SIGNAL]; + node.applyValueToInputSignal(node, value); +}; \ No newline at end of file diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/button.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/button.component.spec.ts index d0dbdb996d..dacb5d08c4 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/button.component.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/button.component.spec.ts @@ -1,11 +1,7 @@ -import { ɵSIGNAL as SIGNAL } from '@angular/core'; + import { createHostFactory, SpectatorHost } from '@ngneat/spectator/vitest'; import { ButtonComponent } from '../components'; - -const setInputSignal = (inputSignal: () => T, value: T) => { - const node = inputSignal[SIGNAL]; - node.applyValueToInputSignal(node, value); -}; +import { setInputSignal } from './utils'; describe('ButtonComponent', () => { let spectator: SpectatorHost; From 5b601373d0ed26f15a1be6940f61bd872eca26a4 Mon Sep 17 00:00:00 2001 From: sumeyye Date: Tue, 30 Jun 2026 15:52:09 +0300 Subject: [PATCH 8/8] fix: warnings in the UI tests relating the ng v22 upgrade --- .../lib/tests/form-submit.directive.spec.ts | 7 ++-- .../lib/tests/permission.directive.spec.ts | 2 +- .../replaceable-template.directive.spec.ts | 12 ++----- .../lib/tests/breadcrumb.component.spec.ts | 7 ++++ .../src/lib/tests/ellipsis.directive.spec.ts | 33 +++++-------------- .../src/lib/tests/loading.directive.spec.ts | 4 +-- .../src/lib/tests/modal.component.spec.ts | 22 +++++++++---- 7 files changed, 41 insertions(+), 46 deletions(-) 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 6e7d40f625..93ae717a65 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,8 +1,8 @@ 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 { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; +import { setInputSignal } from './utils'; describe('FormSubmitDirective', () => { @@ -21,7 +21,7 @@ describe('FormSubmitDirective', () => { vi.useFakeTimers(); spectator = createDirective( - '
form content
', + '
form content
', { hostProps: { submitEventFn, @@ -30,6 +30,7 @@ describe('FormSubmitDirective', () => { }, ); directive = spectator.directive; + setInputSignal(directive.debounce, 20); }); afterEach(() => { @@ -44,7 +45,7 @@ describe('FormSubmitDirective', () => { }); test('should have 20ms debounce time', () => { - expect(directive.debounce()).toBe(200); + expect(directive.debounce()).toBe(20); }); test('should dispatch submit event on keyup event triggered after given debounce time', async () => { 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 152c2a7b32..42c0ee1224 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 @@ -21,7 +21,7 @@ describe('PermissionDirective', () => { beforeEach(() => { spectator = createDirective( - '
', + '
test
', { hostProps: { permission: 'test', runCD: false }, }, diff --git a/npm/ng-packs/packages/core/src/lib/tests/replaceable-template.directive.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/replaceable-template.directive.spec.ts index d31c20ee60..3cdcf72075 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/replaceable-template.directive.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/replaceable-template.directive.spec.ts @@ -1,4 +1,4 @@ -import { Component, inject, input, output, ɵSIGNAL as SIGNAL } from '@angular/core'; +import { Component, inject, input, output } from '@angular/core'; import { Router } from '@angular/router'; import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/vitest'; import { BehaviorSubject } from 'rxjs'; @@ -86,18 +86,12 @@ describe('ReplaceableTemplateDirective', () => { it('should create directive successfully', () => { spectator = createDirective( ` -
+ -
+ `, { detectChanges: false, - hostProps: { - oneWay: { label: 'Test' }, - twoWay: false, - twoWayChange: vi.fn(), - someOutput: vi.fn(), - }, }, ); setInputSignal(spectator.directive.data, { diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/breadcrumb.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/breadcrumb.component.spec.ts index 2a56255023..6ad217d3b8 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/breadcrumb.component.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/breadcrumb.component.spec.ts @@ -24,6 +24,7 @@ const mockRoutes: ABP.Route[] = [ describe('BreadcrumbComponent', () => { let spectator: SpectatorRouting; let routes: RoutesService; + let consoleErrorSpy: ReturnType; const createRouting = createRoutingFactory({ component: RouterOutletComponent, @@ -51,6 +52,7 @@ describe('BreadcrumbComponent', () => { }, registerLocaleFn: () => Promise.resolve(), skipGetAppConfiguration: true, + skipInitAuthService: true, }), ), { @@ -102,10 +104,15 @@ describe('BreadcrumbComponent', () => { beforeAll(() => setupComponentResources('../components/breadcrumb', import.meta.url)); beforeEach(() => { + consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined); spectator = createRouting(); routes = spectator.inject(RoutesService); }); + afterEach(() => { + consoleErrorSpy.mockRestore(); + }); + it('should create component', async () => { routes.add(mockRoutes); await spectator.router.navigateByUrl('/identity/users'); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/ellipsis.directive.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/ellipsis.directive.spec.ts index 438675fb8c..6bb7b18888 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/ellipsis.directive.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/ellipsis.directive.spec.ts @@ -11,12 +11,9 @@ describe('EllipsisDirective', () => { }); beforeEach(() => { - spectator = createDirective( - '
test content
', - { - detectChanges: false, - }, - ); + spectator = createDirective('
test content
', { + detectChanges: false, + }); setInputSignal(spectator.directive.width, '100px'); setInputSignal(spectator.directive.enabled, true); setInputSignal(spectator.directive.title, 'title'); @@ -56,15 +53,9 @@ describe('EllipsisDirective when title is not specified', () => { }); beforeEach(() => { - spectator = createDirective( - '
test content
', - { - hostProps: { - title: undefined, - width: '100px', - }, - }, - ); + spectator = createDirective('
test content
', { + detectChanges: false, + }); setInputSignal(spectator.directive.width, '100px'); setInputSignal(spectator.directive.enabled, true); setInputSignal(spectator.directive.title, undefined); @@ -87,15 +78,9 @@ describe('EllipsisDirective when width is not given', () => { }); beforeEach(() => { - spectator = createDirective( - '
test content
', - { - hostProps: { - title: 'test title', - width: undefined, - }, - }, - ); + spectator = createDirective('
test content
', { + detectChanges: false, + }); setInputSignal(spectator.directive.width, undefined); setInputSignal(spectator.directive.enabled, true); setInputSignal(spectator.directive.title, 'test title'); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/loading.directive.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/loading.directive.spec.ts index 638584612f..b7322ec63e 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/loading.directive.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/loading.directive.spec.ts @@ -21,7 +21,7 @@ describe('LoadingDirective', () => { describe('default', () => { beforeEach(() => { - spectator = createDirective('
Testing Loading Directive
', { + spectator = createDirective('
Testing Loading Directive
', { hostProps: { loading: true }, }); }); @@ -78,7 +78,7 @@ describe('LoadingDirective', () => { describe('with a component selector', () => { beforeEach(() => { - spectator = createDirective('', { + spectator = createDirective('', { hostProps: { loading: true }, }); }); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts index 0caef230ca..39003890f9 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts @@ -1,10 +1,11 @@ import { ConfirmationService } from '@abp/ng.theme.shared'; import { CoreTestingModule } from '@abp/ng.core/testing'; -import { Component, EventEmitter, Input } from '@angular/core'; +import { Component, EventEmitter, input } from '@angular/core'; import { createComponentFactory, Spectator } from '@ngneat/spectator/vitest'; import { Confirmation } from '@abp/ng.theme.shared'; import { firstValueFrom, Subject, timer } from 'rxjs'; import { ModalComponent } from '../components/modal/modal.component'; +import { setInputSignal } from './utils'; import { setupComponentResources } from './utils'; @Component({ @@ -22,8 +23,8 @@ import { setupComponentResources } from './utils'; imports: [ModalComponent] }) class TestHostComponent { - @Input() visible = false; - @Input() busy = false; + visible = input(false); + busy = input(false); visibleChange = new EventEmitter(); } @@ -33,10 +34,13 @@ const disappearFn = vi.fn(); describe('ModalComponent', () => { let spectator: Spectator; let createComponent: ReturnType>; + let consoleErrorSpy: ReturnType; beforeAll(() => setupComponentResources('../components/modal', import.meta.url)); beforeEach(() => { + consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined); + // Create component factory in beforeEach to ensure beforeAll has run if (!createComponent) { createComponent = createComponentFactory({ @@ -60,20 +64,24 @@ describe('ModalComponent', () => { disappearFn.mockClear(); }); + afterEach(() => { + consoleErrorSpy.mockRestore(); + }); + it('should create component', () => { expect(spectator.component).toBeTruthy(); }); it('should handle visible input', () => { - spectator.setInput('visible', true); + setInputSignal(spectator.component.visible, true); spectator.detectChanges(); - expect(spectator.component.visible).toBe(true); + expect(spectator.component.visible()).toBe(true); }); it('should handle busy input', () => { - spectator.setInput('busy', true); + setInputSignal(spectator.component.busy, true); spectator.detectChanges(); - expect(spectator.component.busy).toBe(true); + expect(spectator.component.busy()).toBe(true); }); it('should have visibleChange emitter', () => {