Skip to content
Merged
51 changes: 26 additions & 25 deletions npm/ng-packs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -49,30 +49,31 @@
"private": true,
"devDependencies": {
"@abp/ng.theme.lepton-x": "~5.5.0",
"@abp/ng.schematics": "~10.5.0",
"@abp/utils": "~10.5.0",
"@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",
Expand Down Expand Up @@ -159,4 +160,4 @@
"dependencies": {
"openid-client": "^6.6.4"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Comment on lines +34 to 38
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);
}
}
Original file line number Diff line number Diff line change
@@ -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<AutofocusDirective>;
Expand All @@ -11,26 +11,32 @@ describe('AutofocusDirective', () => {
});

beforeEach(() => {
spectator = createDirective('<input [autofocus]="10" />', {
hostProps: {},
});
vi.useFakeTimers();

spectator = createDirective('<input autofocus />');
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();
});
});
52 changes: 30 additions & 22 deletions npm/ng-packs/packages/core/src/lib/tests/capsLock.directive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TrackCapsLockDirective } from '../directives';

@Component({
template: ` <input (abpCapsLock)="capsLock = $event" /> `,
template: ` <input abpCapsLock /> `,
imports: [TrackCapsLockDirective],
})
class TestComponent {
Expand All @@ -14,6 +14,20 @@ class TestComponent {
describe('TrackCapsLockDirective', () => {
let fixture: ComponentFixture<TestComponent>;
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(() => {
fixture = TestBed.configureTestingModule({
Expand All @@ -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);
});
});
Original file line number Diff line number Diff line change
@@ -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<InputEventDebounceDirective>;
Expand All @@ -13,26 +13,41 @@ describe('InputEventDebounceDirective', () => {
});

beforeEach(() => {
spectator = createDirective('<input (input.debounce)="inputEventFn()" [debounce]="20" />', {
vi.useFakeTimers();

spectator = createDirective('<input input.debounce (input.debounce)="inputEventFn()" />', {
hostProps: { inputEventFn },
});
directive = spectator.directive;
input = spectator.query('input');
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();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
});
});
Loading
Loading