Browse Source

fix: UI tests in the core package relating the ng v22 upgrade

pull/25729/head
sumeyye 3 months ago
parent
commit
f291993d35
  1. 30
      npm/ng-packs/packages/core/src/lib/tests/autofocus.directive.spec.ts
  2. 52
      npm/ng-packs/packages/core/src/lib/tests/capsLock.directive.spec.ts
  3. 31
      npm/ng-packs/packages/core/src/lib/tests/debounce.directive.spec.ts
  4. 6
      npm/ng-packs/packages/core/src/lib/tests/environment-utils.spec.ts
  5. 3
      npm/ng-packs/packages/core/src/lib/tests/factory-utils.spec.ts
  6. 181
      npm/ng-packs/packages/core/src/lib/tests/for.directive.spec.ts
  7. 21
      npm/ng-packs/packages/core/src/lib/tests/form-submit.directive.spec.ts
  8. 34
      npm/ng-packs/packages/core/src/lib/tests/local-storage.service.spec.ts
  9. 15
      npm/ng-packs/packages/core/src/lib/tests/permission.directive.spec.ts
  10. 68
      npm/ng-packs/packages/core/src/lib/tests/permission.guard.spec.ts
  11. 44
      npm/ng-packs/packages/core/src/lib/tests/replaceable-template.directive.spec.ts
  12. 40
      npm/ng-packs/packages/core/src/lib/tests/show-password-directive.spec.ts
  13. 9
      npm/ng-packs/packages/core/src/lib/tests/stop-propagation.directive.spec.ts

30
npm/ng-packs/packages/core/src/lib/tests/autofocus.directive.spec.ts

@ -1,6 +1,6 @@
import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/vitest'; import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/vitest';
import { AutofocusDirective } from '../directives/autofocus.directive'; import { AutofocusDirective } from '../directives/autofocus.directive';
import { timer } from 'rxjs'; import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
describe('AutofocusDirective', () => { describe('AutofocusDirective', () => {
let spectator: SpectatorDirective<AutofocusDirective>; let spectator: SpectatorDirective<AutofocusDirective>;
@ -11,26 +11,32 @@ describe('AutofocusDirective', () => {
}); });
beforeEach(() => { beforeEach(() => {
spectator = createDirective('<input [autofocus]="10" />', { vi.useFakeTimers();
hostProps: {},
}); spectator = createDirective('<input autofocus />');
directive = spectator.directive; directive = spectator.directive;
input = spectator.query('input'); input = spectator.query('input');
}); });
afterEach(() => {
if (vi.isFakeTimers()) {
vi.runOnlyPendingTimers();
}
vi.useRealTimers();
});
test('should be created', () => { test('should be created', () => {
expect(directive).toBeTruthy(); expect(directive).toBeTruthy();
}); });
test('should have 10ms delay', () => { test('should have 0ms delay', () => {
expect(directive.delay).toBe(10); expect(directive.delay()).toBe(0);
}); });
test('should focus element after given delay', () => { test('should focus element after default delay', () => {
timer(0).subscribe(() => expect('input').not.toBeFocused()); expect(input).not.toBeFocused();
timer(11).subscribe(() => {
expect('input').toBeFocused(); vi.runOnlyPendingTimers();
expect.hasAssertions(); expect(input).toBeFocused();
});
}); });
}); });

52
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'; import { TrackCapsLockDirective } from '../directives';
@Component({ @Component({
template: ` <input (abpCapsLock)="capsLock = $event" /> `, template: ` <input abpCapsLock /> `,
imports: [TrackCapsLockDirective], imports: [TrackCapsLockDirective],
}) })
class TestComponent { class TestComponent {
@ -14,6 +14,20 @@ class TestComponent {
describe('TrackCapsLockDirective', () => { describe('TrackCapsLockDirective', () => {
let fixture: ComponentFixture<TestComponent>; let fixture: ComponentFixture<TestComponent>;
let des: DebugElement[]; let des: DebugElement[];
let directive: TrackCapsLockDirective;
let emitSpy: ReturnType<typeof vi.spyOn>;
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(() => { beforeEach(() => {
fixture = TestBed.configureTestingModule({ fixture = TestBed.configureTestingModule({
@ -23,35 +37,29 @@ describe('TrackCapsLockDirective', () => {
fixture.detectChanges(); fixture.detectChanges();
des = fixture.debugElement.queryAll(By.directive(TrackCapsLockDirective)); 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'])( test.each(['keydown', 'keyup'])(
'is %p works when press capslock and is emit status', 'is %p works when press capslock and is emit status',
eventName => { eventName => {
const event = new KeyboardEvent(eventName, { const event = createCapsLockEvent(eventName, true);
key: 'CapsLock', eventName === 'keydown' ? directive.onKeyDown(event) : directive.onKeyUp(event);
modifierCapsLock: true, expect(emitSpy).toHaveBeenCalledWith(true);
});
window.dispatchEvent(event);
fixture.detectChanges();
expect(fixture.componentInstance.capsLock).toBe(true);
}, },
); );
test.each(['keydown', 'keyup'])('is %p detect the change capslock is emit status', eventName => { test.each(['keydown', 'keyup'])('is %p detect the change capslock is emit status', eventName => {
const trueEvent = new KeyboardEvent(eventName, { const trueEvent = createCapsLockEvent(eventName, true);
key: 'CapsLock', eventName === 'keydown' ? directive.onKeyDown(trueEvent) : directive.onKeyUp(trueEvent);
modifierCapsLock: true, expect(emitSpy).toHaveBeenCalledWith(true);
}); const falseEvent = createCapsLockEvent(eventName, false);
window.dispatchEvent(trueEvent); eventName === 'keydown' ? directive.onKeyDown(falseEvent) : directive.onKeyUp(falseEvent);
fixture.detectChanges(); expect(emitSpy).toHaveBeenLastCalledWith(false);
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);
}); });
}); });

31
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 { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/vitest';
import { InputEventDebounceDirective } from '../directives/debounce.directive'; import { InputEventDebounceDirective } from '../directives/debounce.directive';
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
describe('InputEventDebounceDirective', () => { describe('InputEventDebounceDirective', () => {
let spectator: SpectatorDirective<InputEventDebounceDirective>; let spectator: SpectatorDirective<InputEventDebounceDirective>;
@ -13,7 +13,9 @@ describe('InputEventDebounceDirective', () => {
}); });
beforeEach(() => { beforeEach(() => {
spectator = createDirective('<input (input.debounce)="inputEventFn()" [debounce]="20" />', { vi.useFakeTimers();
spectator = createDirective('<input input.debounce (input.debounce)="inputEventFn()" />', {
hostProps: { inputEventFn }, hostProps: { inputEventFn },
}); });
directive = spectator.directive; directive = spectator.directive;
@ -21,18 +23,31 @@ describe('InputEventDebounceDirective', () => {
inputEventFn.mockClear(); inputEventFn.mockClear();
}); });
afterEach(() => {
if (vi.isFakeTimers()) {
vi.runOnlyPendingTimers();
}
vi.useRealTimers();
});
test('should be created', () => { test('should be created', () => {
expect(directive).toBeTruthy(); expect(directive).toBeTruthy();
}); });
test('should have 20ms debounce time', () => { test('should have 300ms debounce time', () => {
expect(directive.debounce).toBe(20); 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); spectator.dispatchFakeEvent('input', 'input', true);
timer(0).subscribe(() => expect(inputEventFn).not.toHaveBeenCalled()); expect(emitSpy).not.toHaveBeenCalled();
await firstValueFrom(timer(21));
expect(inputEventFn).toHaveBeenCalled(); vi.advanceTimersByTime(299);
expect(emitSpy).not.toHaveBeenCalled();
vi.advanceTimersByTime(1);
expect(emitSpy).toHaveBeenCalled();
}); });
}); });

6
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 () => { it('should handle request error gracefully and use local environment', async () => {
const injector = spectator.inject(Injector); const injector = spectator.inject(Injector);
const injectorSpy = jest.spyOn(injector, 'get'); const injectorSpy = vi.spyOn(injector, 'get');
const http = spectator.inject(HttpClient); const http = spectator.inject(HttpClient);
const requestSpy = jest.spyOn(http, 'request'); const requestSpy = vi.spyOn(http, 'request');
const environmentService = spectator.inject(EnvironmentService); const environmentService = spectator.inject(EnvironmentService);
const setStateSpy = jest.spyOn(environmentService, 'setState'); const setStateSpy = vi.spyOn(environmentService, 'setState');
injectorSpy.mockReturnValueOnce(environmentService); injectorSpy.mockReturnValueOnce(environmentService);
injectorSpy.mockReturnValueOnce(http); injectorSpy.mockReturnValueOnce(http);

3
npm/ng-packs/packages/core/src/lib/tests/factory-utils.spec.ts

@ -28,11 +28,12 @@ describe('LazyModuleFactory', () => {
const injector = TestBed.inject(Injector); const injector = TestBed.inject(Injector);
const moduleRef = factory.create(injector); const moduleRef = factory.create(injector);
expect('componentFactoryResolver' in moduleRef).toBe(true);
expect('destroy' in moduleRef).toBe(true); expect('destroy' in moduleRef).toBe(true);
expect('injector' in moduleRef).toBe(true); expect('injector' in moduleRef).toBe(true);
expect('instance' in moduleRef).toBe(true); expect('instance' in moduleRef).toBe(true);
expect('onDestroy' in moduleRef).toBe(true); expect('onDestroy' in moduleRef).toBe(true);
expect(moduleRef.instance).toBeInstanceOf(Module);
expect(moduleRef.injector.get('foo')).toBe('bar');
}); });
}); });
}); });

181
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 { SpectatorDirective, createDirectiveFactory } from '@ngneat/spectator/vitest';
import { ForDirective } from '../directives/for.directive'; import { ForDirective } from '../directives/for.directive';
const setInputSignal = <T>(inputSignal: () => T, value: T) => {
const node = inputSignal[SIGNAL];
node.applyValueToInputSignal(node, value);
};
describe('ForDirective', () => { describe('ForDirective', () => {
let spectator: SpectatorDirective<ForDirective>; let spectator: SpectatorDirective<ForDirective>;
let directive: ForDirective; let directive: ForDirective;
@ -9,12 +15,44 @@ describe('ForDirective', () => {
directive: 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<Record<keyof ForDirective, unknown>>) => {
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', () => { describe('basic', () => {
beforeEach(() => { beforeEach(() => {
spectator = createDirective('<ul><li *abpFor="let item of items">{{ item }}</li></ul>', { spectator = createDirective('<ul><ng-template abpFor let-item><li>{{ item }}</li></ng-template></ul>');
hostProps: { items },
});
directive = spectator.directive; directive = spectator.directive;
setDirectiveInputs({ items });
}); });
test('should be created', () => { test('should be created', () => {
@ -29,166 +67,116 @@ describe('ForDirective', () => {
}); });
test('should sync the DOM when change items', () => { test('should sync the DOM when change items', () => {
directive.items = [10, 11, 12]; setDirectiveInputs({ items: [10, 11, 12] });
directive['vcRef'].clear();
directive['lastItemsRef'] = null;
directive['differ'] = null;
directive.ngOnChanges();
spectator.detectChanges();
const elements = spectator.queryAll('li');
const elements = spectator.queryAll('li');
expect(elements[1]).toHaveText('11'); expect(elements[1]).toHaveText('11');
expect(elements).toHaveLength(3); expect(elements).toHaveLength(3);
}); });
test('should sync the DOM when add an item', () => { test('should sync the DOM when add an item', () => {
directive.items = [...items, 6]; setDirectiveInputs({ items: [...items, 6] });
directive['vcRef'].clear();
directive['lastItemsRef'] = null;
directive['differ'] = null;
directive.ngOnChanges();
spectator.detectChanges();
const elements = spectator.queryAll('li');
const elements = spectator.queryAll('li');
expect(elements[6]).toHaveText('6'); expect(elements[6]).toHaveText('6');
expect(elements).toHaveLength(7); expect(elements).toHaveLength(7);
}); });
}); });
describe('trackBy', () => { describe('trackBy', () => {
const trackByFn = (_, item) => item; const trackByFn = (_: number, item: number) => item;
beforeEach(() => { beforeEach(() => {
spectator = createDirective( spectator = createDirective('<ul><ng-template abpFor let-item><li>{{ item }}</li></ng-template></ul>');
'<ul><li *abpFor="let item of items; trackBy: trackByFn">{{ item }}</li></ul>',
{
hostProps: { items, trackByFn },
},
);
directive = spectator.directive; directive = spectator.directive;
setDirectiveInputs({ items, trackBy: trackByFn });
}); });
test('should be setted the trackBy', () => { test('should be setted the trackBy', () => {
expect(directive.trackBy).toEqual(trackByFn); expect(directive.trackBy()).toEqual(trackByFn);
}); });
}); });
describe('with basic order', () => { describe('with basic order', () => {
beforeEach(() => { beforeEach(() => {
spectator = createDirective( spectator = createDirective('<ul><ng-template abpFor let-item><li>{{ item }}</li></ng-template></ul>');
`<ul>
<li
*abpFor="let item of [3,6,2];
orderDir: 'ASC'">
{{ item }}
</li>
</ul>`,
);
directive = spectator.directive; directive = spectator.directive;
setDirectiveInputs({ items: [3, 6, 2], orderDir: 'ASC' });
}); });
test('should order by asc', () => { test('should order by asc', () => {
const elements = spectator.queryAll('li'); expect(getTexts()).toEqual(['2', '3', '6']);
expect(elements.map(el => el.textContent.trim())).toEqual(['2', '3', '6']);
}); });
}); });
describe('with order', () => { describe('with order', () => {
beforeEach(() => { beforeEach(() => {
spectator = createDirective( spectator = createDirective(
`<ul> '<ul><ng-template abpFor let-item><li>{{ item.value }}</li></ng-template></ul>',
<li
*abpFor="let item of [{value: 3}, {value: 6}, {value: 2}];
orderBy: 'value';
orderDir: orderDir">
{{ item.value }}
</li>
</ul>`,
{
hostProps: { orderDir: 'ASC' },
},
); );
directive = spectator.directive; directive = spectator.directive;
setDirectiveInputs({
items: [{ value: 3 }, { value: 6 }, { value: 2 }],
orderBy: 'value',
orderDir: 'ASC',
});
}); });
test('should order by asc', () => { test('should order by asc', () => {
const elements = spectator.queryAll('li'); expect(getTexts()).toEqual(['2', '3', '6']);
expect(elements.map(el => el.textContent.trim())).toEqual(['2', '3', '6']);
}); });
test('should order by desc', () => { test('should order by desc', () => {
directive.orderDir = 'DESC'; setDirectiveInputs({ orderDir: 'DESC' });
directive['vcRef'].clear();
directive['lastItemsRef'] = null;
directive['differ'] = null;
directive.ngOnChanges();
spectator.detectChanges();
const elements = spectator.queryAll('li'); expect(getTexts()).toEqual(['6', '3', '2']);
expect(elements.map(el => el.textContent.trim())).toEqual(['6', '3', '2']);
}); });
}); });
describe('with filter', () => { describe('with filter', () => {
beforeEach(() => { beforeEach(() => {
spectator = createDirective( spectator = createDirective(
`<ul> '<ul><ng-template abpFor let-item><li>{{ item.value }}</li></ng-template></ul>',
<li
*abpFor="let item of [{value: 'test'}, {value: 'abp'}, {value: 'volo'}];
filterBy: 'value';
filterVal: filterVal">
{{ item.value }}
</li>
</ul>`,
{
hostProps: { filterVal: '' },
},
); );
directive = spectator.directive; directive = spectator.directive;
setDirectiveInputs({
items: [{ value: 'test' }, { value: 'abp' }, { value: 'volo' }],
filterBy: 'value',
filterVal: '',
});
}); });
test('should not filter when filterVal is empty,', () => { test('should not filter when filterVal is empty,', () => {
const elements = spectator.queryAll('li'); expect(getTexts()).toEqual(['test', 'abp', 'volo']);
expect(elements.map(el => el.textContent.trim())).toEqual(['test', 'abp', 'volo']);
}); });
test('should be filtered', () => { test('should be filtered', () => {
directive.filterVal = 'volo'; setDirectiveInputs({ filterVal: 'volo' });
directive['vcRef'].clear();
directive['lastItemsRef'] = null;
directive['differ'] = null;
directive.ngOnChanges();
spectator.detectChanges();
expect(spectator.query('li')).toHaveText('volo'); expect(spectator.query('li')).toHaveText('volo');
}); });
test('should not show an element when filter value not match to any text', () => { test('should not show an element when filter value not match to any text', () => {
directive.filterVal = 'volos'; setDirectiveInputs({ filterVal: 'volos' });
directive.ngOnChanges();
spectator.detectChanges();
const elements = spectator.queryAll('li'); expect(spectator.queryAll('li')).toHaveLength(0);
expect(elements).toHaveLength(0);
}); });
}); });
describe('with empty ref', () => { describe('with empty ref', () => {
beforeEach(() => { beforeEach(() => {
spectator = createDirective( spectator = createDirective(`
`<ul> <ul>
<li <ng-template abpFor let-item>
*abpFor="let item of items; <li>{{ item.value }}</li>
emptyRef: empty"> </ng-template>
{{ item.value }}
</li>
<ng-template #empty>No records found</ng-template> <ng-template #empty>No records found</ng-template>
</ul>`, </ul>
{ `);
hostProps: { items: [] },
},
);
directive = spectator.directive; directive = spectator.directive;
const [, emptyRef] = getTemplateRefs();
setDirectiveInputs({ items: [], emptyRef });
}); });
test('should display the empty ref', () => { test('should display the empty ref', () => {
@ -200,12 +188,7 @@ describe('ForDirective', () => {
expect(spectator.query('ul')).toHaveText('No records found'); expect(spectator.query('ul')).toHaveText('No records found');
expect(spectator.queryAll('li')).toHaveLength(0); expect(spectator.queryAll('li')).toHaveLength(0);
directive.items = [0]; setDirectiveInputs({ items: [{ value: 0 }] });
directive['vcRef'].clear();
directive['lastItemsRef'] = null;
directive['differ'] = null;
directive.ngOnChanges();
spectator.detectChanges();
expect(spectator.query('ul')).not.toHaveText('No records found'); expect(spectator.query('ul')).not.toHaveText('No records found');
expect(spectator.queryAll('li')).toHaveLength(1); expect(spectator.queryAll('li')).toHaveLength(1);

21
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 { FormSubmitDirective } from '../directives/form-submit.directive';
import { FormsModule, ReactiveFormsModule, FormGroup } from '@angular/forms'; import { FormsModule, ReactiveFormsModule, FormGroup } from '@angular/forms';
import { timer, firstValueFrom } from 'rxjs'; import { timer, firstValueFrom } from 'rxjs';
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
describe('FormSubmitDirective', () => { describe('FormSubmitDirective', () => {
let spectator: SpectatorDirective<FormSubmitDirective>; let spectator: SpectatorDirective<FormSubmitDirective>;
@ -16,6 +18,8 @@ describe('FormSubmitDirective', () => {
}); });
beforeEach(() => { beforeEach(() => {
vi.useFakeTimers();
spectator = createDirective( spectator = createDirective(
'<form [formGroup]="formGroup" (ngSubmit)="submitEventFn()" [debounce]="20">form content</form>', '<form [formGroup]="formGroup" (ngSubmit)="submitEventFn()" [debounce]="20">form content</form>',
{ {
@ -28,12 +32,19 @@ describe('FormSubmitDirective', () => {
directive = spectator.directive; directive = spectator.directive;
}); });
afterEach(() => {
if (vi.isFakeTimers()) {
vi.runOnlyPendingTimers();
}
vi.useRealTimers();
});
test('should be created', () => { test('should be created', () => {
expect(directive).toBeTruthy(); expect(directive).toBeTruthy();
}); });
test('should have 20ms debounce time', () => { 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 () => { test('should dispatch submit event on keyup event triggered after given debounce time', async () => {
@ -44,8 +55,12 @@ describe('FormSubmitDirective', () => {
cancelable: true, cancelable: true,
}); });
form?.dispatchEvent(event); form?.dispatchEvent(event);
timer(0).subscribe(() => expect(submitEventFn).not.toHaveBeenCalled()); expect(submitEventFn).not.toHaveBeenCalled();
await firstValueFrom(timer(directive.debounce + 10));
vi.advanceTimersByTime(199);
expect(submitEventFn).not.toHaveBeenCalled();
vi.advanceTimersByTime(1);
expect(submitEventFn).toHaveBeenCalled(); expect(submitEventFn).toHaveBeenCalled();
}); });
}); });

34
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', () => { describe('LocalStorageService', () => {
let service: AbpLocalStorageService; let service: AbpLocalStorageService;
let localStorageMock: Storage;
beforeEach(() => { 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({}); TestBed.configureTestingModule({});
service = TestBed.inject(AbpLocalStorageService); service = TestBed.inject(AbpLocalStorageService);
}); });
afterEach(() => {
vi.unstubAllGlobals();
});
it('should be created', () => { it('should be created', () => {
expect(service).toBeTruthy(); expect(service).toBeTruthy();
}); });
it('should be called getItem', () => { it('should be called getItem', () => {
const spy = vi.spyOn(service, 'getItem');
service.getItem('test'); service.getItem('test');
expect(spy).toHaveBeenCalled(); expect(localStorageMock.getItem).toHaveBeenCalledWith('test');
}); });
it('should be called setItem', () => { it('should be called setItem', () => {
const spy = vi.spyOn(service, 'setItem');
service.setItem('test', 'value'); service.setItem('test', 'value');
expect(spy).toHaveBeenCalled(); expect(localStorageMock.setItem).toHaveBeenCalledWith('test', 'value');
}); });
it('should be called removeItem', () => { it('should be called removeItem', () => {
const spy = vi.spyOn(service, 'removeItem');
service.removeItem('test'); service.removeItem('test');
expect(spy).toHaveBeenCalled(); expect(localStorageMock.removeItem).toHaveBeenCalledWith('test');
}); });
it('should be called clear', () => { it('should be called clear', () => {
const spy = vi.spyOn(service, 'clear');
service.clear(); service.clear();
expect(spy).toHaveBeenCalled(); expect(localStorageMock.clear).toHaveBeenCalled();
}); });
it('should be called key', () => { it('should be called key', () => {
const spy = vi.spyOn(service, 'key');
service.key(0); service.key(0);
expect(spy).toHaveBeenCalled(); expect(localStorageMock.key).toHaveBeenCalledWith(0);
}); });
it('should be called length', () => { it('should be called length', () => {
const spy = vi.spyOn(service, 'length', 'get'); vi.stubGlobal('localStorage', { ...localStorageMock, length: 1 });
service.length; service.length;
expect(spy).toHaveBeenCalled(); expect(service.length).toBe(1);
}); });
}); });

15
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 { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/vitest';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { PermissionDirective } from '../directives/permission.directive'; import { PermissionDirective } from '../directives/permission.directive';
import { PermissionService } from '../services/permission.service'; import { PermissionService } from '../services/permission.service';
import { QUEUE_MANAGER } from '../tokens/queue.token'; import { QUEUE_MANAGER } from '../tokens/queue.token';
const setInputSignal = <T>(inputSignal: () => T, value: T) => {
const node = inputSignal[SIGNAL];
node.applyValueToInputSignal(node, value);
};
describe('PermissionDirective', () => { describe('PermissionDirective', () => {
let spectator: SpectatorDirective<PermissionDirective>; let spectator: SpectatorDirective<PermissionDirective>;
let directive: PermissionDirective; let directive: PermissionDirective;
@ -44,19 +49,19 @@ describe('PermissionDirective', () => {
it('should handle permission input', () => { it('should handle permission input', () => {
grantedPolicy$.next(false); grantedPolicy$.next(false);
directive.condition = 'new-permission'; setInputSignal(directive.condition, 'new-permission');
directive.ngOnChanges(); directive.ngOnChanges();
grantedPolicy$.next(true); grantedPolicy$.next(true);
expect(directive).toBeTruthy(); expect(directive).toBeTruthy();
expect(directive.condition).toBe('new-permission'); expect(directive.condition()).toBe('new-permission');
}); });
it('should handle runChangeDetection input', () => { it('should handle runChangeDetection input', () => {
grantedPolicy$.next(false); grantedPolicy$.next(false);
directive.runChangeDetection = true; setInputSignal(directive.runChangeDetection, true);
directive.ngOnChanges(); directive.ngOnChanges();
grantedPolicy$.next(true); grantedPolicy$.next(true);
expect(directive).toBeTruthy(); expect(directive).toBeTruthy();
expect(directive.runChangeDetection).toBe(true); expect(directive.runChangeDetection()).toBe(true);
}); });
}); });

68
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 { Component } from '@angular/core';
import { provideRouter, Route, Router } from '@angular/router'; import { provideRouter, Route, Router } from '@angular/router';
import { RouterTestingHarness } from '@angular/router/testing'; import { RouterTestingHarness } from '@angular/router/testing';
@ -9,8 +7,8 @@ import { of } from 'rxjs';
import { permissionGuard } from '../guards/permission.guard'; import { permissionGuard } from '../guards/permission.guard';
import { HttpErrorReporterService } from '../services/http-error-reporter.service'; import { HttpErrorReporterService } from '../services/http-error-reporter.service';
import { PermissionService } from '../services/permission.service'; import { PermissionService } from '../services/permission.service';
import { provideAbpCore, withOptions } from '../providers';
import { AuthService } from '../abstracts'; import { AuthService } from '../abstracts';
import { ConfigStateService, RouteBasedCultureUrlService, RoutesService } from '../services';
@Component({ template: '' }) @Component({ template: '' })
class DummyComponent {} class DummyComponent {}
@ -20,8 +18,11 @@ class DummyComponent {}
describe('authGuard', () => { describe('authGuard', () => {
let permissionService: SpyObject<PermissionService>; let permissionService: SpyObject<PermissionService>;
let httpErrorReporter: SpyObject<HttpErrorReporterService>; let httpErrorReporter: SpyObject<HttpErrorReporterService>;
let routesService: Pick<RoutesService, 'find'>;
let routeCultureUrl: Pick<RouteBasedCultureUrlService, 'getRoutePathForMatching'>;
let configStateService: Pick<ConfigStateService, 'getAll$'>;
const mockOAuthService = { const authService = {
isAuthenticated: true, isAuthenticated: true,
}; };
@ -53,46 +54,30 @@ describe('authGuard', () => {
beforeEach(() => { beforeEach(() => {
httpErrorReporter = createSpyObject(HttpErrorReporterService); httpErrorReporter = createSpyObject(HttpErrorReporterService);
permissionService = createSpyObject(PermissionService); 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({ TestBed.configureTestingModule({
providers: [ providers: [
provideHttpClient(), { provide: AuthService, useValue: authService },
provideHttpClientTesting(),
{ provide: AuthService, useValue: mockOAuthService },
{ provide: PermissionService, useValue: permissionService }, { provide: PermissionService, useValue: permissionService },
{ provide: HttpErrorReporterService, useValue: httpErrorReporter }, { provide: HttpErrorReporterService, useValue: httpErrorReporter },
{ provide: RoutesService, useValue: routesService },
{ provide: RouteBasedCultureUrlService, useValue: routeCultureUrl },
{ provide: ConfigStateService, useValue: configStateService },
provideRouter(routes), 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 () => { it('should return true when the grantedPolicy is true', async () => {
permissionService.getGrantedPolicy$.andReturn(of(true)); permissionService.getGrantedPolicy$.andReturn(of(true));
await RouterTestingHarness.create('/dummy'); await RouterTestingHarness.create('/dummy');
@ -103,17 +88,24 @@ describe('authGuard', () => {
it('should return false and report an error when the grantedPolicy is false', () => { it('should return false and report an error when the grantedPolicy is false', () => {
permissionService.getGrantedPolicy$.andReturn(of(false)); permissionService.getGrantedPolicy$.andReturn(of(false));
expect(permissionService.getGrantedPolicy$).toBeDefined(); return RouterTestingHarness.create('/dummy').then(() => {
expect(httpErrorReporter.reportError).toBeDefined(); expect(TestBed.inject(Router).url).toEqual('/');
expect(httpErrorReporter.reportError).toHaveBeenCalledWith({ status: 403 });
});
}); });
it('should check the requiredPolicy from RoutesService', async () => { 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 => { permissionService.getGrantedPolicy$.mockImplementation(policy => {
return of(policy === 'TestPolicy'); 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(); expect(httpErrorReporter.reportError).not.toHaveBeenCalled();
}); });

44
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 { Router } from '@angular/router';
import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/vitest'; import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/vitest';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
@ -6,34 +6,37 @@ import { ReplaceableTemplateDirective } from '../directives/replaceable-template
import { ReplaceableComponents } from '../models/replaceable-components'; import { ReplaceableComponents } from '../models/replaceable-components';
import { ReplaceableComponentsService } from '../services/replaceable-components.service'; import { ReplaceableComponentsService } from '../services/replaceable-components.service';
const setInputSignal = <T>(inputSignal: () => T, value: T) => {
const node = inputSignal[SIGNAL];
node.applyValueToInputSignal(node, value);
};
@Component({ @Component({
selector: 'abp-default-component', selector: 'abp-default-component',
template: ' <p>default</p> ', template: ' <p>default</p> ',
exportAs: 'abpDefaultComponent' exportAs: 'abpDefaultComponent',
}) })
class DefaultComponent { class DefaultComponent {
@Input() onOneWay = input<any>();
oneWay;
@Input() twoWay = input<boolean>();
twoWay: boolean;
readonly twoWayChange = output<boolean>(); readonly twoWayChange = output<boolean>();
readonly someOutput = output<string>(); readonly someOutput = output<string>();
setTwoWay(value) { setTwoWay(value) {
this.twoWay = value; setInputSignal(this.twoWay, value);
this.twoWayChange.emit(value); this.twoWayChange.emit(value);
} }
} }
@Component({ @Component({
selector: 'abp-external-component', selector: 'abp-external-component',
template: ' <p>external</p> ' template: ' <p>external</p> ',
}) })
class ExternalComponent { class ExternalComponent {
data = inject<ReplaceableComponents.ReplaceableTemplateData<any, any>>('REPLACEABLE_DATA' as any, { optional: true })!; data = inject<ReplaceableComponents.ReplaceableTemplateData<any, any>>('REPLACEABLE_DATA' as any)!;
} }
describe('ReplaceableTemplateDirective', () => { describe('ReplaceableTemplateDirective', () => {
@ -54,11 +57,12 @@ describe('ReplaceableTemplateDirective', () => {
beforeEach(() => { beforeEach(() => {
spectator = createDirective( spectator = createDirective(
` `
<div *abpReplaceableTemplate="{inputs: {oneWay: {value: oneWay}, twoWay: {value: twoWay, twoWay: true}}, outputs: {twoWayChange: twoWayChange, someOutput: someOutput}, componentKey: 'TestModule.TestComponent'}; let initTemplate = initTemplate"> <ng-template abpReplaceableTemplate let-initTemplate="initTemplate">
<abp-default-component #defaultComponent="abpDefaultComponent"></abp-default-component> <abp-default-component #defaultComponent="abpDefaultComponent"></abp-default-component>
</div> </ng-template>
`, `,
{ {
detectChanges: false,
hostProps: { hostProps: {
oneWay: { label: 'Test' }, oneWay: { label: 'Test' },
twoWay: false, 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', () => { it('should create directive successfully', () => {
@ -83,6 +96,7 @@ describe('ReplaceableTemplateDirective', () => {
</div> </div>
`, `,
{ {
detectChanges: false,
hostProps: { hostProps: {
oneWay: { label: 'Test' }, oneWay: { label: 'Test' },
twoWay: false, 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(); expect(spectator.directive).toBeTruthy();
}); });
}); });

40
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 { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { ShowPasswordDirective } from '../directives'; import { ShowPasswordDirective } from '../directives';
@Component({ @Component({
template: ` <input [abpShowPassword]="true" /> template: ` <input abpShowPassword />
<input [abpShowPassword]="false" /> <input abpShowPassword />
<input /> <input />
<input [abpShowPassword]="showPassword" />`, <input abpShowPassword />`,
imports: [ShowPasswordDirective], imports: [ShowPasswordDirective],
}) })
class TestComponent { class TestComponent {}
showPassword = false;
} const setInputSignal = <T>(inputSignal: () => T, value: T) => {
const node = inputSignal[SIGNAL];
node.applyValueToInputSignal(node, value);
};
describe('ShowPasswordDirective', () => { describe('ShowPasswordDirective', () => {
let fixture: ComponentFixture<TestComponent>; let fixture: ComponentFixture<TestComponent>;
let des: DebugElement[]; let des: DebugElement[];
let desAll: 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(() => { beforeEach(() => {
fixture = TestBed.configureTestingModule({ fixture = TestBed.configureTestingModule({
imports: [TestComponent], imports: [TestComponent],
}).createComponent(TestComponent); }).createComponent(TestComponent);
fixture.detectChanges(); detectChanges();
des = fixture.debugElement.queryAll(By.directive(ShowPasswordDirective)); des = fixture.debugElement.queryAll(By.directive(ShowPasswordDirective));
desAll = fixture.debugElement.queryAll(By.all()); 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', () => { it('should have three input has ShowPasswordDirective elements', () => {
@ -52,11 +66,7 @@ describe('ShowPasswordDirective', () => {
const input = des[2].nativeElement; const input = des[2].nativeElement;
expect(input.type).toBe('password'); expect(input.type).toBe('password');
fixture.componentInstance.showPassword = true; setShowPassword(2, true);
const cdr = fixture.componentRef.injector.get(ChangeDetectorRef);
cdr.markForCheck();
cdr.detectChanges();
expect(input.type).toBe('text'); expect(input.type).toBe('text');
}); });

9
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 { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/vitest';
import { StopPropagationDirective } from '../directives/stop-propagation.directive'; import { StopPropagationDirective } from '../directives/stop-propagation.directive';
import { beforeEach, describe, expect, test, vi } from 'vitest';
describe('StopPropagationDirective', () => { describe('StopPropagationDirective', () => {
let spectator: SpectatorDirective<StopPropagationDirective>; let spectator: SpectatorDirective<StopPropagationDirective>;
@ -13,12 +14,13 @@ describe('StopPropagationDirective', () => {
beforeEach(() => { beforeEach(() => {
spectator = createDirective( spectator = createDirective(
'<div (click)="parentClickEventFn()"><a (click.stop)="childClickEventFn()" >Link</a></div>', '<div (click)="parentClickEventFn()"><a click.stop>Link</a></div>',
{ {
hostProps: { parentClickEventFn, childClickEventFn }, hostProps: { parentClickEventFn },
}, },
); );
directive = spectator.directive; directive = spectator.directive;
directive.stopPropEvent.subscribe(childClickEventFn);
link = spectator.query('a'); link = spectator.query('a');
childClickEventFn.mockClear(); childClickEventFn.mockClear();
parentClickEventFn.mockClear(); parentClickEventFn.mockClear();
@ -29,8 +31,7 @@ describe('StopPropagationDirective', () => {
}); });
test('should not call click event of parent when child element is clicked', () => { test('should not call click event of parent when child element is clicked', () => {
spectator.setHostInput({ parentClickEventFn, childClickEventFn }); spectator.click(link);
spectator.click('a');
spectator.detectChanges(); spectator.detectChanges();
expect(childClickEventFn).toHaveBeenCalled(); expect(childClickEventFn).toHaveBeenCalled();
expect(parentClickEventFn).not.toHaveBeenCalled(); expect(parentClickEventFn).not.toHaveBeenCalled();

Loading…
Cancel
Save