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 d81550edaa..517d8b08a2 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 @@ -1,5 +1,5 @@ import { StartLoader, StopLoader } from '@abp/ng.core'; -import { ChangeDetectorRef, Component, Input, OnDestroy } from '@angular/core'; +import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core'; import { NavigationEnd, NavigationError, NavigationStart, Router } from '@angular/router'; import { takeUntilDestroy } from '@ngx-validate/core'; import { Actions, ofActionSuccessful } from '@ngxs/store'; @@ -20,55 +20,62 @@ import { filter } from 'rxjs/operators'; > `, - styleUrls: ['./loader-bar.component.scss'] + styleUrls: ['./loader-bar.component.scss'], }) -export class LoaderBarComponent implements OnDestroy { +export class LoaderBarComponent implements OnDestroy, OnInit { + @Input() + containerClass = 'abp-loader-bar'; + + @Input() + color = '#77b6ff'; + + @Input() + isLoading = false; + + progressLevel = 0; + + interval: Subscription; + + timer: Subscription; + + intervalPeriod = 350; + + stopDelay = 820; + + @Input() + filter = (action: StartLoader | StopLoader) => action.payload.url.indexOf('openid-configuration') < 0; + get boxShadow(): string { return `0 0 10px rgba(${this.color}, 0.5)`; } - constructor(private actions: Actions, private router: Router, private cdRef: ChangeDetectorRef) { - actions + constructor(private actions: Actions, private router: Router, private cdRef: ChangeDetectorRef) {} + + ngOnInit() { + this.actions .pipe( ofActionSuccessful(StartLoader, StopLoader), filter(this.filter), - takeUntilDestroy(this) + takeUntilDestroy(this), ) .subscribe(action => { if (action instanceof StartLoader) this.startLoading(); else this.stopLoading(); }); - router.events + this.router.events .pipe( filter( event => - event instanceof NavigationStart || event instanceof NavigationEnd || event instanceof NavigationError + event instanceof NavigationStart || event instanceof NavigationEnd || event instanceof NavigationError, ), - takeUntilDestroy(this) + takeUntilDestroy(this), ) .subscribe(event => { if (event instanceof NavigationStart) this.startLoading(); else this.stopLoading(); }); } - @Input() - containerClass = 'abp-loader-bar'; - - @Input() - color = '#77b6ff'; - - @Input() - isLoading = false; - - progressLevel = 0; - - interval: Subscription; - - timer: Subscription; - - @Input() - filter = (action: StartLoader | StopLoader) => action.payload.url.indexOf('openid-configuration') < 0; ngOnDestroy() { this.interval.unsubscribe(); @@ -78,7 +85,7 @@ export class LoaderBarComponent implements OnDestroy { if (this.isLoading || this.progressLevel !== 0) return; this.isLoading = true; - this.interval = interval(350).subscribe(() => { + this.interval = interval(this.intervalPeriod).subscribe(() => { if (this.progressLevel < 75) { this.progressLevel += Math.random() * 10; } else if (this.progressLevel < 90) { @@ -98,7 +105,7 @@ export class LoaderBarComponent implements OnDestroy { this.isLoading = false; if (this.timer && !this.timer.closed) return; - this.timer = timer(820).subscribe(() => { + this.timer = timer(this.stopDelay).subscribe(() => { this.progressLevel = 0; this.cdRef.detectChanges(); }); 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 new file mode 100644 index 0000000000..639a224474 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/loader-bar.component.spec.ts @@ -0,0 +1,92 @@ +import { Router, RouteReuseStrategy, NavigationStart, NavigationEnd, NavigationError } from '@angular/router'; +import { createHostFactory, SpectatorHost, SpyObject } from '@ngneat/spectator/jest'; +import { Actions, NgxsModule, Store } from '@ngxs/store'; +import { Subject, Subscription, Observable, Subscriber, timer } from 'rxjs'; +import { LoaderBarComponent } from '../components/loader-bar/loader-bar.component'; +import { StartLoader, StopLoader } from '@abp/ng.core'; +import { HttpRequest } from '@angular/common/http'; + +describe('LoaderBarComponent', () => { + let spectator: SpectatorHost; + let router: SpyObject; + const events$ = new Subject(); + + const createHost = createHostFactory({ + component: LoaderBarComponent, + mocks: [Router], + imports: [NgxsModule.forRoot()], + detectChanges: false, + }); + + beforeEach(() => { + spectator = createHost(''); + spectator.component.intervalPeriod = 1; + spectator.component.stopDelay = 1; + router = spectator.get(Router); + (router as any).events = events$; + }); + + it('should initial variable values are correct', () => { + spectator.component.interval = new Subscription(); + expect(spectator.component.containerClass).toBe('abp-loader-bar'); + expect(spectator.component.color).toBe('#77b6ff'); + }); + + it('should increase the progressLevel maximum 10 point when value is 0', done => { + spectator.detectChanges(); + spectator.get(Store).dispatch(new StartLoader(new HttpRequest('GET', 'test'))); + setTimeout(() => { + expect(spectator.component.progressLevel > 0 && spectator.component.progressLevel < 10).toBeTruthy(); + done(); + }, 2); + }); + + it('should be interval unsubscribed', done => { + spectator.detectChanges(); + spectator.get(Store).dispatch(new StartLoader(new HttpRequest('GET', 'test'))); + expect(spectator.component.interval.closed).toBe(false); + + timer(400).subscribe(() => { + expect(spectator.component.interval.closed).toBe(true); + done(); + }); + }); + + it('should start and stop the loading with navigation', done => { + spectator.detectChanges(); + (router as any).events.next(new NavigationStart(1, 'test')); + expect(spectator.component.interval.closed).toBe(false); + + (router as any).events.next(new NavigationEnd(1, 'test', 'test')); + (router as any).events.next(new NavigationError(1, 'test', 'test')); + expect(spectator.component.progressLevel).toBe(100); + + timer(2).subscribe(() => { + expect(spectator.component.progressLevel).toBe(0); + done(); + }); + }); + + it('should stop the loading with navigation', done => { + spectator.detectChanges(); + (router as any).events.next(new NavigationStart(1, 'test')); + expect(spectator.component.interval.closed).toBe(false); + + spectator.get(Store).dispatch(new StopLoader(new HttpRequest('GET', 'test'))); + expect(spectator.component.progressLevel).toBe(100); + + timer(2).subscribe(() => { + expect(spectator.component.progressLevel).toBe(0); + done(); + }); + }); + + describe('#startLoading', () => { + it('should return when isLoading is true', done => { + spectator.detectChanges(); + (router as any).events.next(new NavigationStart(1, 'test')); + (router as any).events.next(new NavigationStart(1, 'test')); + done(); + }); + }); +});