diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.ts index 7ccc5c97bd..b06475ac7a 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.ts @@ -41,6 +41,7 @@ export class ErrorComponent implements AfterViewInit, OnDestroy { if (this.customComponent) { const customComponentRef = this.cfRes.resolveComponentFactory(this.customComponent).create(null); customComponentRef.instance.errorStatus = this.status; + customComponentRef.instance.destroy$ = this.destroy$; this.containerRef.nativeElement.appendChild((customComponentRef.hostView as EmbeddedViewRef).rootNodes[0]); customComponentRef.changeDetectorRef.detectChanges(); } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts b/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts index 5f75b2ecd5..ced1980067 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts @@ -39,7 +39,7 @@ export const DEFAULT_ERROR_MESSAGES = { details: 'The resource requested could not found on the server.', }, defaultError500: { - title: '500', + title: 'Internal server error', details: 'Error detail not sent by server.', }, }; @@ -106,11 +106,11 @@ export class ErrorHandler { : this.showError( { key: 'AbpAccount::DefaultErrorMessage404', - defaultValue: DEFAULT_ERROR_MESSAGES.defaultError404.title, + defaultValue: DEFAULT_ERROR_MESSAGES.defaultError404.details, }, { key: 'AbpAccount::DefaultErrorMessage404Detail', - defaultValue: DEFAULT_ERROR_MESSAGES.defaultError404.details, + defaultValue: DEFAULT_ERROR_MESSAGES.defaultError404.title, }, ); break; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/services/confirmation.service.ts b/npm/ng-packs/packages/theme-shared/src/lib/services/confirmation.service.ts index 6aa592767d..cceedc22cb 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/services/confirmation.service.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/services/confirmation.service.ts @@ -25,7 +25,6 @@ export class ConfirmationService extends AbstractToaster { options?: Confirmation.Options, ): Observable { this.listenToEscape(); - return super.show(message, title, severity, options); } 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 index f54d52440d..05e303e4c9 100644 --- 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 @@ -3,6 +3,7 @@ import { ErrorComponent } from '../components/error/error.component'; import { LocalizationPipe } from '@abp/ng.core'; import { Store } from '@ngxs/store'; import { Renderer2, ElementRef } from '@angular/core'; +import { Subject } from 'rxjs'; describe('ErrorComponent', () => { let spectator: SpectatorHost; @@ -16,21 +17,26 @@ describe('ErrorComponent', () => { ], }); - beforeEach(() => (spectator = createHost(''))); + beforeEach(() => { + spectator = createHost(''); + spectator.component.destroy$ = new Subject(); + }); describe('#destroy', () => { - it('should remove the dom', () => { - const renderer = spectator.get(Renderer2); - const rendererSpy = jest.spyOn(renderer, 'removeChild'); - spectator.component.renderer = renderer; + it('should be call when pressed the esc key', done => { + spectator.component.destroy$.subscribe(res => { + done(); + }); + + spectator.keyboard.pressEscape(); + }); - const elementRef = spectator.get(ElementRef); - spectator.component.elementRef = elementRef; - spectator.component.host = spectator.hostComponent; + it('should be call when clicked the close button', done => { + spectator.component.destroy$.subscribe(res => { + done(); + }); - spectator.click('button#abp-close-button'); - spectator.detectChanges(); - expect(rendererSpy).toHaveBeenCalledWith(spectator.hostComponent, elementRef.nativeElement); + spectator.click('#abp-close-button'); }); }); }); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts index 764197f1e3..d3265691de 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts @@ -1,21 +1,23 @@ import { CoreModule, RestOccurError, RouterOutletComponent } from '@abp/ng.core'; import { Location } from '@angular/common'; import { HttpErrorResponse, HttpHeaders } from '@angular/common/http'; -import { Component } from '@angular/core'; +import { Component, NgModule } from '@angular/core'; import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator/jest'; import { NgxsModule, Store } from '@ngxs/store'; import { DEFAULT_ERROR_MESSAGES, ErrorHandler } from '../handlers'; import { ThemeSharedModule } from '../theme-shared.module'; +import { MessageService } from 'primeng/components/common/messageservice'; +import { RouterError, RouterDataResolved } from '@ngxs/router-plugin'; +import { NavigationError, ResolveEnd } from '@angular/router'; @Component({ selector: 'abp-dummy', template: 'dummy works! ' }) class DummyComponent { - constructor(public errorHandler: ErrorHandler, public store: Store) {} + constructor(public errorHandler: ErrorHandler) {} } +let spectator: SpectatorRouting; +let store: Store; describe('ErrorHandler', () => { - let spectator: SpectatorRouting; - let store: Store; - const createComponent = createRoutingFactory({ component: DummyComponent, imports: [CoreModule, ThemeSharedModule.forRoot(), NgxsModule.forRoot([])], @@ -25,7 +27,7 @@ describe('ErrorHandler', () => { beforeEach(() => { spectator = createComponent(); - store = spectator.component.store; + store = spectator.get(Store); const abpError = document.querySelector('abp-error'); if (abpError) document.body.removeChild(abpError); @@ -33,27 +35,19 @@ describe('ErrorHandler', () => { it('should display the error component when server error occurs', () => { store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 500 }))); - spectator.detectChanges(); expect(document.querySelector('.error-template')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError500.title); - expect(document.querySelector('.error-details')).toHaveText( - DEFAULT_ERROR_MESSAGES.defaultError500.details.defaultValue, - ); + expect(document.querySelector('.error-details')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError500.details); }); it('should display the error component when authorize error occurs', () => { store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 403 }))); - spectator.detectChanges(); expect(document.querySelector('.error-template')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError403.title); expect(document.querySelector('.error-details')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError403.details); }); it('should display the error component when unknown error occurs', () => { store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 0, statusText: 'Unknown Error' }))); - spectator.detectChanges(); - expect(document.querySelector('.error-template')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultErrorUnknown.title); - expect(document.querySelector('.error-details')).toHaveText( - DEFAULT_ERROR_MESSAGES.defaultErrorUnknown.details.defaultValue, - ); + expect(document.querySelector('.error-template')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError.title); }); it('should display the confirmation when not found error occurs', () => { @@ -110,3 +104,79 @@ describe('ErrorHandler', () => { expect(spectator.query('.abp-confirm-body')).toHaveText('test detail'); }); }); + +@Component({ + selector: 'abp-dummy-error', + template: '

{{errorStatus}}

', +}) +class DummyErrorComponent { + errorStatus; + destroy$; +} + +@NgModule({ + declarations: [DummyErrorComponent], + exports: [DummyErrorComponent], + entryComponents: [DummyErrorComponent], +}) +class ErrorModule {} + +describe('ErrorHandler with custom error component', () => { + const createComponent = createRoutingFactory({ + component: DummyComponent, + imports: [ + CoreModule, + ThemeSharedModule.forRoot({ + httpErrorConfig: { errorScreen: { component: DummyErrorComponent, forWhichErrors: [401, 403, 404, 500] } }, + }), + NgxsModule.forRoot([]), + ErrorModule, + ], + stubsEnabled: false, + routes: [{ path: '', component: DummyComponent }, { path: 'account/login', component: RouterOutletComponent }], + }); + + beforeEach(() => { + spectator = createComponent(); + store = spectator.get(Store); + + const abpError = document.querySelector('abp-error'); + if (abpError) document.body.removeChild(abpError); + }); + + describe('Custom error component', () => { + it('should create when occur 401', () => { + store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 401 }))); + expect(document.querySelector('abp-dummy-error')).toBeTruthy(); + expect(document.querySelector('p')).toHaveExactText('401'); + }); + + it('should create when occur 403', () => { + store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 403 }))); + expect(document.querySelector('p')).toHaveExactText('403'); + }); + + it('should create when occur 404', () => { + store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 404 }))); + expect(document.querySelector('p')).toHaveExactText('404'); + }); + + it('should create when dispatched the RouterError', () => { + store.dispatch(new RouterError(null, null, new NavigationError(1, 'test', 'Cannot match'))); + expect(document.querySelector('p')).toHaveExactText('404'); + store.dispatch(new RouterDataResolved(null, new ResolveEnd(1, 'test', 'test', null))); + }); + + it('should create when occur 500', () => { + store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 500 }))); + expect(document.querySelector('p')).toHaveExactText('500'); + }); + + it('should be destroyed when click the close button', () => { + store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 500 }))); + document.querySelector('#close-dummy').click(); + spectator.detectChanges(); + expect(document.querySelector('abp-dummy-error')).toBeFalsy(); + }); + }); +});