From a143db7819dedd4237dc8f866293193f83ab47b4 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Thu, 31 Oct 2019 09:40:40 +0300 Subject: [PATCH 1/9] test: add breadcrumb.component.spec --- .../lib/tests/breadcrumb.component.spec.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 npm/ng-packs/packages/theme-shared/src/lib/tests/breadcrumb.component.spec.ts 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 new file mode 100644 index 0000000000..10c5a6fb2a --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/breadcrumb.component.spec.ts @@ -0,0 +1,26 @@ +import { LocalizationPipe } from '@abp/ng.core'; +import { RouterTestingModule } from '@angular/router/testing'; +import { createComponentFactory, Spectator, SpyObject } from '@ngneat/spectator/jest'; +import { Store } from '@ngxs/store'; +import { BreadcrumbComponent } from '../components/breadcrumb/breadcrumb.component'; + +describe('BreadcrumbComponent', () => { + let spectator: Spectator; + let store: SpyObject; + const createComponent = createComponentFactory({ + component: BreadcrumbComponent, + mocks: [Store], + imports: [RouterTestingModule], + declarations: [LocalizationPipe], + detectChanges: false, + }); + + beforeEach(() => { + spectator = createComponent(); + store = spectator.get(Store); + }); + + it('should display the breadcrumb', () => { + store.selectSnapshot.andCallFake(selector => selector({ LeptonLayoutState: {} })); + }); +}); From 104ebb4a32584361020a021d01fed6a8ce2e7d2b Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Thu, 31 Oct 2019 09:50:53 +0300 Subject: [PATCH 2/9] test: create mock strategy for breadcrumb --- .../src/lib/tests/breadcrumb.component.spec.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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 10c5a6fb2a..cae05df3e5 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 @@ -1,15 +1,16 @@ -import { LocalizationPipe } from '@abp/ng.core'; +import { LocalizationPipe, ABP } from '@abp/ng.core'; import { RouterTestingModule } from '@angular/router/testing'; import { createComponentFactory, Spectator, SpyObject } from '@ngneat/spectator/jest'; import { Store } from '@ngxs/store'; import { BreadcrumbComponent } from '../components/breadcrumb/breadcrumb.component'; +import { Router } from '@angular/router'; describe('BreadcrumbComponent', () => { let spectator: Spectator; let store: SpyObject; const createComponent = createComponentFactory({ component: BreadcrumbComponent, - mocks: [Store], + mocks: [Store, Router], imports: [RouterTestingModule], declarations: [LocalizationPipe], detectChanges: false, @@ -21,6 +22,9 @@ describe('BreadcrumbComponent', () => { }); it('should display the breadcrumb', () => { - store.selectSnapshot.andCallFake(selector => selector({ LeptonLayoutState: {} })); + const router = spectator.get(Router); + (router as any).url = '/identity/users'; + store.selectSnapshot.andReturn({ name: 'Identity', children: [{ name: 'Users', path: 'users' }] } as ABP.FullRoute); + spectator.detectChanges(); }); }); From 9432fc0af70577573a4b1390306d674316b20232 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Thu, 31 Oct 2019 10:01:05 +0300 Subject: [PATCH 3/9] test: add new expects to breadcrumb --- .../src/lib/tests/breadcrumb.component.spec.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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 cae05df3e5..cb37315f53 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,7 +24,14 @@ describe('BreadcrumbComponent', () => { it('should display the breadcrumb', () => { const router = spectator.get(Router); (router as any).url = '/identity/users'; - store.selectSnapshot.andReturn({ name: 'Identity', children: [{ name: 'Users', path: 'users' }] } as ABP.FullRoute); + store.selectSnapshot.andReturn({ + name: 'Identity', + children: [{ name: 'Users', path: 'users' }], + }); + spectator.component.show = true; spectator.detectChanges(); + spectator.detectComponentChanges(); + expect(spectator.component.segments).toEqual(['Identity', 'Users']); + // expect(spectator.queryAll('li')).toHaveLength(3); }); }); From c49ecf2b1eb8c74eff216c320e1fe5d53e3846ca Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Thu, 31 Oct 2019 11:44:32 +0300 Subject: [PATCH 4/9] test(theme-shared): add new expects to breadcrumb test --- .../breadcrumb/breadcrumb.component.ts | 8 ++--- .../lib/tests/breadcrumb.component.spec.ts | 30 +++++++++++++------ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.ts index 1b0d4a5a4c..90b49e8efb 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.ts @@ -5,18 +5,18 @@ import { ConfigState, ABP } from '@abp/ng.core'; @Component({ selector: 'abp-breadcrumb', - templateUrl: './breadcrumb.component.html' + templateUrl: './breadcrumb.component.html', }) export class BreadcrumbComponent implements OnInit { show: boolean; segments: string[] = []; - constructor(private router: Router, private store: Store) { - this.show = !!this.store.selectSnapshot(state => state.LeptonLayoutState); - } + constructor(private router: Router, private store: Store) {} ngOnInit(): void { + this.show = !!this.store.selectSnapshot(state => state.LeptonLayoutState); + const splittedUrl = this.router.url.split('/').filter(chunk => chunk); const currentUrl: ABP.FullRoute = this.store.selectSnapshot(ConfigState.getRoute(splittedUrl[0])); 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 cb37315f53..85b3729bf7 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 @@ -1,9 +1,15 @@ -import { LocalizationPipe, ABP } from '@abp/ng.core'; -import { RouterTestingModule } from '@angular/router/testing'; +import { LocalizationPipe } from '@abp/ng.core'; +import { Directive } from '@angular/core'; +import { Router } from '@angular/router'; import { createComponentFactory, Spectator, SpyObject } from '@ngneat/spectator/jest'; import { Store } from '@ngxs/store'; import { BreadcrumbComponent } from '../components/breadcrumb/breadcrumb.component'; -import { Router } from '@angular/router'; + +@Directive({ + // tslint:disable-next-line: directive-selector + selector: '[routerLink], [routerLinkActive]', +}) +class DummyRouterLinkDirective {} describe('BreadcrumbComponent', () => { let spectator: Spectator; @@ -11,8 +17,8 @@ describe('BreadcrumbComponent', () => { const createComponent = createComponentFactory({ component: BreadcrumbComponent, mocks: [Store, Router], - imports: [RouterTestingModule], - declarations: [LocalizationPipe], + imports: [], + declarations: [LocalizationPipe, DummyRouterLinkDirective], detectChanges: false, }); @@ -24,14 +30,20 @@ describe('BreadcrumbComponent', () => { it('should display the breadcrumb', () => { const router = spectator.get(Router); (router as any).url = '/identity/users'; - store.selectSnapshot.andReturn({ + store.selectSnapshot.mockReturnValueOnce({ LeptonLayoutState: {} }); + store.selectSnapshot.mockReturnValueOnce({ name: 'Identity', children: [{ name: 'Users', path: 'users' }], }); - spectator.component.show = true; + // for abpLocalization + store.selectSnapshot.mockReturnValueOnce('Identity'); + store.selectSnapshot.mockReturnValueOnce('Users'); spectator.detectChanges(); - spectator.detectComponentChanges(); + expect(spectator.component.segments).toEqual(['Identity', 'Users']); - // expect(spectator.queryAll('li')).toHaveLength(3); + const elements = spectator.queryAll('li'); + expect(elements).toHaveLength(3); + expect(elements[1]).toHaveText('Identity'); + expect(elements[2]).toHaveText('Users'); }); }); From 09f804b2ae5d5e9a2ef207854dd161a8583ec7d5 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Thu, 31 Oct 2019 12:37:33 +0300 Subject: [PATCH 5/9] test(theme-shared): add error.component.spec --- .../lib/components/error/error.component.html | 4 +-- .../src/lib/tests/error.component.spec.ts | 36 +++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 npm/ng-packs/packages/theme-shared/src/lib/tests/error.component.spec.ts diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.html b/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.html index 8954910674..f367d49e89 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.html +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.html @@ -1,5 +1,5 @@ -
-
diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/error.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/error.component.spec.ts new file mode 100644 index 0000000000..f54d52440d --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/error.component.spec.ts @@ -0,0 +1,36 @@ +import { SpectatorHost, createHostFactory } from '@ngneat/spectator/jest'; +import { ErrorComponent } from '../components/error/error.component'; +import { LocalizationPipe } from '@abp/ng.core'; +import { Store } from '@ngxs/store'; +import { Renderer2, ElementRef } from '@angular/core'; + +describe('ErrorComponent', () => { + let spectator: SpectatorHost; + const createHost = createHostFactory({ + component: ErrorComponent, + declarations: [LocalizationPipe], + mocks: [Store], + providers: [ + { provide: Renderer2, useValue: { removeChild: () => null } }, + { provide: ElementRef, useValue: { nativeElement: document.createElement('div') } }, + ], + }); + + beforeEach(() => (spectator = createHost(''))); + + describe('#destroy', () => { + it('should remove the dom', () => { + const renderer = spectator.get(Renderer2); + const rendererSpy = jest.spyOn(renderer, 'removeChild'); + spectator.component.renderer = renderer; + + const elementRef = spectator.get(ElementRef); + spectator.component.elementRef = elementRef; + spectator.component.host = spectator.hostComponent; + + spectator.click('button#abp-close-button'); + spectator.detectChanges(); + expect(rendererSpy).toHaveBeenCalledWith(spectator.hostComponent, elementRef.nativeElement); + }); + }); +}); From 413994fd8186d3aa5f748f11f68cfcd9889e5d1b Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 1 Nov 2019 02:03:33 +0300 Subject: [PATCH 6/9] test(theme-shared): add loader-bar.component.spec --- .../loader-bar/loader-bar.component.ts | 63 +++++++------ .../lib/tests/loader-bar.component.spec.ts | 92 +++++++++++++++++++ 2 files changed, 127 insertions(+), 28 deletions(-) create mode 100644 npm/ng-packs/packages/theme-shared/src/lib/tests/loader-bar.component.spec.ts 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(); + }); + }); +}); From ebff2cfcac73fd950198aa9831f5cf9bed336687 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 1 Nov 2019 10:58:12 +0300 Subject: [PATCH 7/9] test(theme-shared): add modal.component.spec --- .../lib/components/modal/modal.component.html | 1 + .../lib/components/modal/modal.component.ts | 6 +- .../src/lib/tests/modal.component.spec.ts | 126 ++++++++++++++++++ 3 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 npm/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html index ecd2fea59e..56ec59cf06 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html @@ -5,6 +5,7 @@ id="abp-modal-dialog" class="modal-dialog modal-{{ size }}" role="document" + [class.modal-dialog-centered]="centered" [@dialog]="isModalOpen" #abpModalContent > diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts index 718de17394..6f52f5a95e 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts @@ -13,10 +13,10 @@ import { } from '@angular/core'; import { fromEvent, Subject } from 'rxjs'; import { debounceTime, filter, takeUntil } from 'rxjs/operators'; +import { dialogAnimation, fadeAnimation } from '../../animations/modal.animations'; import { Toaster } from '../../models/toaster'; import { ConfirmationService } from '../../services/confirmation.service'; import { ButtonComponent } from '../button/button.component'; -import { fadeAnimation, dialogAnimation } from '../../animations/modal.animations'; export type ModalSize = 'sm' | 'md' | 'lg' | 'xl'; @@ -134,9 +134,9 @@ export class ModalComponent implements OnDestroy { .pipe( takeUntil(this.destroy$), debounceTime(150), - filter((key: KeyboardEvent) => key && key.code === 'Escape'), + filter((key: KeyboardEvent) => key && key.key === 'Escape'), ) - .subscribe(_ => { + .subscribe(() => { this.close(); }); 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 new file mode 100644 index 0000000000..e91a7db48c --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts @@ -0,0 +1,126 @@ +import { LocalizationPipe } from '@abp/ng.core'; +import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest'; +import { Store } from '@ngxs/store'; +import { MessageService } from 'primeng/components/common/messageservice'; +import { ToastModule } from 'primeng/toast'; +import { ConfirmationComponent, ModalComponent, ButtonComponent } from '../components'; + +describe('ModalComponent', () => { + let spectator: SpectatorHost; + let appearFn; + let disappearFn; + const createHost = createHostFactory({ + component: ModalComponent, + imports: [ToastModule], + declarations: [ConfirmationComponent, LocalizationPipe, ButtonComponent], + providers: [MessageService], + mocks: [Store], + }); + + beforeEach(() => { + appearFn = jest.fn(() => null); + disappearFn = jest.fn(() => null); + + spectator = createHost( + ` + +
+
+ + +
+ +
+
+ + + + + Submit + +
+ `, + { + hostProps: { + visible: true, + busy: false, + ngDirty: true, + appearFn, + disappearFn, + }, + }, + ); + }); + + it('should be created', () => { + expect(spectator.query('div.modal')).toBeTruthy(); + expect(spectator.query('div.modal-backdrop')).toBeTruthy(); + expect(spectator.query('div#abp-modal-dialog')).toBeTruthy(); + }); + + it('should works right the inputs', () => { + expect(spectator.query('div.test')).toBeTruthy(); + expect(spectator.query('div.modal-sm')).toBeTruthy(); + expect(spectator.query('div.modal-dialog-centered')).toBeTruthy(); + }); + + it('should emit the appear output', () => { + expect(appearFn).toHaveBeenCalled(); + }); + + it('should emit the disappear output', () => { + spectator.hostComponent.visible = false; + spectator.detectChanges(); + expect(disappearFn).toHaveBeenCalled(); + }); + + it('should open the confirmation popup and works correct', () => { + spectator.click('#abp-modal-close-button'); + expect(disappearFn).not.toHaveBeenCalled(); + + expect(spectator.query('p-toast')).toBeTruthy(); + spectator.click('button#cancel'); + expect(spectator.query('div.modal')).toBeTruthy(); + + spectator.click('#abp-modal-close-button'); + spectator.click('button#confirm'); + expect(spectator.query('div.modal')).toBeFalsy(); + expect(disappearFn).toHaveBeenCalled(); + }); + + it('should close with the abpClose', done => { + spectator.hostComponent.ngDirty = false; + spectator.detectChanges(); + setTimeout(() => { + spectator.click('#abp-close'); + expect(disappearFn).toHaveBeenCalled(); + done(); + }, 10); + }); + + it('should close with esc key', done => { + spectator.hostComponent.ngDirty = false; + spectator.detectChanges(); + setTimeout(() => { + spectator.dispatchKeyboardEvent(document.body, 'keyup', 'Escape'); + }, 0); + setTimeout(() => { + expect(spectator.component.visible).toBe(false); + done(); + }, 200); + }); + + it('should not close when busy is true', done => { + setTimeout(() => { + spectator.hostComponent.busy = true; + spectator.hostComponent.ngDirty = false; + spectator.detectChanges(); + spectator.click('#abp-modal-close-button'); + expect(disappearFn).not.toHaveBeenCalled(); + expect(spectator.component.abpSubmit.loading).toBe(true); + done(); + }, 0); + }); +}); From 257ee5edfdfcd886f823fc8b272f345806137bcc Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 1 Nov 2019 17:50:28 +0300 Subject: [PATCH 8/9] test(core): refactor ng-model.component.spec --- .../src/lib/tests/ng-model.component.spec.ts | 40 +++++-------------- 1 file changed, 9 insertions(+), 31 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/tests/ng-model.component.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/ng-model.component.spec.ts index 55c759e60a..c0dc284815 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/ng-model.component.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/ng-model.component.spec.ts @@ -29,57 +29,35 @@ export class TestComponent extends AbstractNgModelComponent implements OnInit { } describe('AbstractNgModelComponent', () => { - let spectator: SpectatorHost; + let spectator: SpectatorHost; const createHost = createHostFactory({ component: TestComponent, declarations: [AbstractNgModelComponent], imports: [FormsModule], - detectChanges: false, }); - test('should pass the value with ngModel', done => { - spectator = createHost('', { + beforeEach(() => { + spectator = createHost('', { hostProps: { val: '1', + override: false, }, }); - - spectator.detectChanges(); - - timer(0).subscribe(() => { - expect(spectator.component.value).toBe('1'); - done(); - }); }); - test.skip('should set the value with ngModel', done => { - spectator = createHost('', { - hostProps: { - val: '2', - }, - }); - - spectator.detectChanges(); - + test('should pass the value with ngModel', done => { timer(0).subscribe(() => { - expect(spectator.hostComponent['val']).toBe('test'); + expect(spectator.component.value).toBe('1'); done(); }); }); - test.skip('should not change value when disable is true', done => { - spectator = createHost('', { - hostProps: { - val: '2', - }, - }); - - spectator.component['val'] = '3'; - spectator.detectChanges(); + test('should set the value with ngModel', done => { + spectator.setHostInput({ val: '2', override: true }); timer(0).subscribe(() => { - expect(spectator.hostComponent['val']).toBe('2'); + expect(spectator.hostComponent.val).toBe('test'); done(); }); }); From a5467abfa68ceaa33c76932b64c55fc2d2a448cb Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 1 Nov 2019 17:51:17 +0300 Subject: [PATCH 9/9] test(theme-shared): add chart.component.spec --- npm/ng-packs/jest.config.js | 2 +- npm/ng-packs/package.json | 1 + .../lib/components/chart/chart.component.ts | 33 ++--- .../src/lib/tests/chart.component.spec.ts | 126 ++++++++++++++++++ npm/ng-packs/test-setup.ts | 1 + 5 files changed, 147 insertions(+), 16 deletions(-) create mode 100644 npm/ng-packs/packages/theme-shared/src/lib/tests/chart.component.spec.ts diff --git a/npm/ng-packs/jest.config.js b/npm/ng-packs/jest.config.js index 580ba0bcdf..8fabbc2a39 100644 --- a/npm/ng-packs/jest.config.js +++ b/npm/ng-packs/jest.config.js @@ -11,7 +11,7 @@ module.exports = { coverageDirectory: '/coverage', coverageReporters: ['html'], preset: 'jest-preset-angular', - setupFilesAfterEnv: ['/test-setup.ts'], + setupFilesAfterEnv: ['/test-setup.ts', 'jest-canvas-mock'], snapshotSerializers: [ 'jest-preset-angular/AngularSnapshotSerializer.js', 'jest-preset-angular/HTMLCommentSerializer.js', diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index 1875b16ac9..84dec91088 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -52,6 +52,7 @@ "font-awesome": "^4.7.0", "husky": "^3.0.9", "jest": "^24.9.0", + "jest-canvas-mock": "^2.1.2", "jest-preset-angular": "^7.1.1", "just-clone": "3.1.0", "just-compare": "^1.3.0", diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/chart/chart.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/chart/chart.component.ts index 45829c8ee4..e019840b7c 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/chart/chart.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/chart/chart.component.ts @@ -6,7 +6,7 @@ import { Input, OnDestroy, Output, - ChangeDetectorRef + ChangeDetectorRef, } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { chartJsLoaded$ } from '../../utils/widget-utils'; @@ -14,7 +14,7 @@ declare const Chart: any; @Component({ selector: 'abp-chart', - templateUrl: './chart.component.html' + templateUrl: './chart.component.html', }) export class ChartComponent implements AfterViewInit, OnDestroy { @Input() type: string; @@ -61,30 +61,33 @@ export class ChartComponent implements AfterViewInit, OnDestroy { ngAfterViewInit() { chartJsLoaded$.subscribe(() => { - try { - // tslint:disable-next-line: no-unused-expression - Chart; - } catch (error) { - console.error(`Chart is not found. Import the Chart from app.module like shown below: - import('chart.js'); - `); - return; - } + this.testChartJs(); this.initChart(); this._initialized = true; }); } + testChartJs() { + try { + // tslint:disable-next-line: no-unused-expression + Chart; + } catch (error) { + throw new Error(`Chart is not found. Import the Chart from app.module like shown below: + import('chart.js'); + `); + } + } + onCanvasClick = event => { if (this.chart) { const element = this.chart.getElementAtEvent(event); const dataset = this.chart.getDatasetAtEvent(event); - if (element && element[0] && dataset) { + if (element && element.length && dataset) { this.onDataSelect.emit({ originalEvent: event, element: element[0], - dataset + dataset, }); } } @@ -99,11 +102,11 @@ export class ChartComponent implements AfterViewInit, OnDestroy { opts.maintainAspectRatio = false; } - this.chart = new Chart(this.el.nativeElement.children[0].children[0], { + this.chart = new Chart(this.canvas, { type: this.type, data: this.data, options: this.options, - plugins: this.plugins + plugins: this.plugins, }); this.cdRef.detectChanges(); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/chart.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/chart.component.spec.ts new file mode 100644 index 0000000000..b1bd0fba16 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/chart.component.spec.ts @@ -0,0 +1,126 @@ +import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest'; +import { ChartComponent } from '../components'; +import { chartJsLoaded$ } from '../utils/widget-utils'; +import { ReplaySubject } from 'rxjs'; +import * as widgetUtils from '../utils/widget-utils'; +// import 'chart.js'; +declare const Chart; + +Object.defineProperty(window, 'getComputedStyle', { + value: () => ({ + getPropertyValue: prop => { + return ''; + }, + }), +}); + +describe('ChartComponent', () => { + let spectator: SpectatorHost; + const createHost = createHostFactory({ component: ChartComponent }); + + beforeEach(() => { + (widgetUtils as any).chartJsLoaded$ = new ReplaySubject(1); + spectator = createHost('', { + hostProps: { + data: { + datasets: [ + { + data: [11], + backgroundColor: ['#FF6384'], + label: 'My dataset', + }, + ], + labels: ['Red'], + }, + }, + }); + }); + + test('should throw error when chart.js is not loaded', () => { + try { + spectator.component.testChartJs(); + } catch (error) { + expect(error.message).toContain('Chart is not found'); + } + }); + + test('should have a success class by default', async done => { + await import('chart.js'); + + chartJsLoaded$.next(); + setTimeout(() => { + expect(spectator.component.chart).toBeTruthy(); + done(); + }, 0); + }); + + describe('#reinit', () => { + it('should call the destroy method', done => { + chartJsLoaded$.next(); + const spy = jest.spyOn(spectator.component.chart, 'destroy'); + spectator.setHostInput({ + data: { + datasets: [ + { + data: [12], + label: 'My dataset', + }, + ], + labels: ['Red'], + }, + }); + spectator.detectChanges(); + setTimeout(() => { + expect(spy).toHaveBeenCalled(); + done(); + }, 0); + }); + }); + + describe('#refresh', () => { + it('should call the update method', done => { + chartJsLoaded$.next(); + const spy = jest.spyOn(spectator.component.chart, 'update'); + spectator.component.refresh(); + setTimeout(() => { + expect(spy).toHaveBeenCalled(); + done(); + }, 0); + }); + }); + + describe('#generateLegend', () => { + it('should call the generateLegend method', done => { + chartJsLoaded$.next(); + const spy = jest.spyOn(spectator.component.chart, 'generateLegend'); + spectator.component.generateLegend(); + setTimeout(() => { + expect(spy).toHaveBeenCalled(); + done(); + }, 0); + }); + }); + + describe('#onCanvasClick', () => { + it('should emit the onDataSelect', done => { + spectator.component.onDataSelect.subscribe(() => { + done(); + }); + + chartJsLoaded$.next(); + jest.spyOn(spectator.component.chart, 'getElementAtEvent').mockReturnValue([document.createElement('div')]); + spectator.click('canvas'); + }); + }); + + describe('#base64Image', () => { + it('should return the base64 image', done => { + chartJsLoaded$.next(); + + setTimeout(() => { + expect(spectator.component.base64Image).toContain('base64'); + done(); + }, 0); + }); + }); +}); diff --git a/npm/ng-packs/test-setup.ts b/npm/ng-packs/test-setup.ts index 8d88704e8f..162c24c518 100644 --- a/npm/ng-packs/test-setup.ts +++ b/npm/ng-packs/test-setup.ts @@ -1 +1,2 @@ import 'jest-preset-angular'; +import 'jest-canvas-mock';