diff --git a/npm/ng-packs/apps/dev-app/src/index.html b/npm/ng-packs/apps/dev-app/src/index.html index cb6d4ae93a..c879c573b1 100644 --- a/npm/ng-packs/apps/dev-app/src/index.html +++ b/npm/ng-packs/apps/dev-app/src/index.html @@ -2,7 +2,7 @@ - DevApp + ABP Dev diff --git a/npm/ng-packs/packages/account/src/lib/components/auth-wrapper/auth-wrapper.component.html b/npm/ng-packs/packages/account/src/lib/components/auth-wrapper/auth-wrapper.component.html index 531c598a5e..145935f7c5 100644 --- a/npm/ng-packs/packages/account/src/lib/components/auth-wrapper/auth-wrapper.component.html +++ b/npm/ng-packs/packages/account/src/lib/components/auth-wrapper/auth-wrapper.component.html @@ -6,7 +6,7 @@
diff --git a/npm/ng-packs/packages/account/src/lib/components/auth-wrapper/auth-wrapper.component.ts b/npm/ng-packs/packages/account/src/lib/components/auth-wrapper/auth-wrapper.component.ts index aadbbfa656..6df49195c4 100644 --- a/npm/ng-packs/packages/account/src/lib/components/auth-wrapper/auth-wrapper.component.ts +++ b/npm/ng-packs/packages/account/src/lib/components/auth-wrapper/auth-wrapper.component.ts @@ -1,8 +1,7 @@ -import { Component, Input, TemplateRef } from '@angular/core'; +import { ConfigState, takeUntilDestroy } from '@abp/ng.core'; +import { Component, Input, OnDestroy, OnInit, TemplateRef } from '@angular/core'; +import { Store } from '@ngxs/store'; import { Account } from '../../models/account'; -import { Select } from '@ngxs/store'; -import { ConfigState } from '@abp/ng.core'; -import { Observable } from 'rxjs'; @Component({ selector: 'abp-auth-wrapper', @@ -10,13 +9,31 @@ import { Observable } from 'rxjs'; exportAs: 'abpAuthWrapper', }) export class AuthWrapperComponent - implements Account.AuthWrapperComponentInputs, Account.AuthWrapperComponentOutputs { - @Select(ConfigState.getSetting('Abp.Account.EnableLocalLogin')) - enableLocalLogin$: Observable<'True' | 'False'>; - + implements + Account.AuthWrapperComponentInputs, + Account.AuthWrapperComponentOutputs, + OnInit, + OnDestroy { @Input() readonly mainContentRef: TemplateRef; @Input() readonly cancelContentRef: TemplateRef; + + enableLocalLogin = true; + + constructor(private store: Store) {} + + ngOnInit() { + this.store + .select(ConfigState.getSetting('Abp.Account.EnableLocalLogin')) + .pipe(takeUntilDestroy(this)) + .subscribe(value => { + if (value) { + this.enableLocalLogin = value.toLowerCase() !== 'false'; + } + }); + } + + ngOnDestroy() {} } diff --git a/npm/ng-packs/packages/account/src/lib/components/change-password/change-password.component.ts b/npm/ng-packs/packages/account/src/lib/components/change-password/change-password.component.ts index 2b66a80b94..e9125de3c3 100644 --- a/npm/ng-packs/packages/account/src/lib/components/change-password/change-password.component.ts +++ b/npm/ng-packs/packages/account/src/lib/components/change-password/change-password.component.ts @@ -72,7 +72,7 @@ export class ChangePasswordComponent required, validatePassword(passwordRulesArr), minLength(requiredLength), - maxLength(32), + maxLength(128), ], }, ], @@ -83,7 +83,7 @@ export class ChangePasswordComponent required, validatePassword(passwordRulesArr), minLength(requiredLength), - maxLength(32), + maxLength(128), ], }, ], diff --git a/npm/ng-packs/packages/account/src/lib/components/login/login.component.html b/npm/ng-packs/packages/account/src/lib/components/login/login.component.html index c448e2a228..09e5d0314d 100644 --- a/npm/ng-packs/packages/account/src/lib/components/login/login.component.html +++ b/npm/ng-packs/packages/account/src/lib/components/login/login.component.html @@ -12,7 +12,7 @@

{{ 'AbpAccount::Login' | abpLocalization }}

- + {{ 'AbpAccount::AreYouANewUser' | abpLocalization }} {{ 'AbpAccount::Register' | abpLocalization diff --git a/npm/ng-packs/packages/account/src/lib/components/login/login.component.ts b/npm/ng-packs/packages/account/src/lib/components/login/login.component.ts index c3b037afa0..21a6ac31a2 100644 --- a/npm/ng-packs/packages/account/src/lib/components/login/login.component.ts +++ b/npm/ng-packs/packages/account/src/lib/components/login/login.component.ts @@ -1,15 +1,12 @@ -import { GetAppConfiguration, ConfigState, SessionState } from '@abp/ng.core'; -import { Component, Inject, Optional } from '@angular/core'; +import { AuthService, SetRemember, ConfigState } from '@abp/ng.core'; +import { ToasterService } from '@abp/ng.theme.shared'; +import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { Navigate } from '@ngxs/router-plugin'; import { Store } from '@ngxs/store'; import { OAuthService } from 'angular-oauth2-oidc'; -import { from, throwError } from 'rxjs'; -import { Options } from '../../models/options'; -import { ToasterService } from '@abp/ng.theme.shared'; -import { catchError, finalize, switchMap, tap } from 'rxjs/operators'; +import { throwError } from 'rxjs'; +import { catchError, finalize } from 'rxjs/operators'; import snq from 'snq'; -import { HttpHeaders } from '@angular/common/http'; const { maxLength, minLength, required } = Validators; @@ -17,47 +14,43 @@ const { maxLength, minLength, required } = Validators; selector: 'abp-login', templateUrl: './login.component.html', }) -export class LoginComponent { +export class LoginComponent implements OnInit { form: FormGroup; inProgress: boolean; + isSelfRegistrationEnabled = true; + constructor( private fb: FormBuilder, private oauthService: OAuthService, private store: Store, private toasterService: ToasterService, - @Optional() @Inject('ACCOUNT_OPTIONS') private options: Options, - ) { - this.oauthService.configure(this.store.selectSnapshot(ConfigState.getOne('environment')).oAuthConfig); - this.oauthService.loadDiscoveryDocument(); + private authService: AuthService, + ) {} + + ngOnInit() { + this.isSelfRegistrationEnabled = + ( + (this.store.selectSnapshot( + ConfigState.getSetting('Abp.Account.IsSelfRegistrationEnabled'), + ) as string) || '' + ).toLowerCase() !== 'false'; this.form = this.fb.group({ username: ['', [required, maxLength(255)]], - password: ['', [required, maxLength(32)]], + password: ['', [required, maxLength(128)]], remember: [false], }); } onSubmit() { if (this.form.invalid) return; - // this.oauthService.setStorage(this.form.value.remember ? localStorage : sessionStorage); this.inProgress = true; - const tenant = this.store.selectSnapshot(SessionState.getTenant); - from( - this.oauthService.fetchTokenUsingPasswordFlow( - this.form.get('username').value, - this.form.get('password').value, - new HttpHeaders({ ...(tenant && tenant.id && { __tenant: tenant.id }) }), - ), - ) + this.authService + .login(this.form.get('username').value, this.form.get('password').value) .pipe( - switchMap(() => this.store.dispatch(new GetAppConfiguration())), - tap(() => { - const redirectUrl = snq(() => window.history.state).redirectUrl || (this.options || {}).redirectUrl || '/'; - this.store.dispatch(new Navigate([redirectUrl])); - }), catchError(err => { this.toasterService.error( snq(() => err.error.error_description) || @@ -69,6 +62,8 @@ export class LoginComponent { }), finalize(() => (this.inProgress = false)), ) - .subscribe(); + .subscribe(() => { + this.store.dispatch(new SetRemember(this.form.get('remember').value)); + }); } } diff --git a/npm/ng-packs/packages/account/src/lib/components/register/register.component.html b/npm/ng-packs/packages/account/src/lib/components/register/register.component.html index 4a0f75d010..f803d473cd 100644 --- a/npm/ng-packs/packages/account/src/lib/components/register/register.component.html +++ b/npm/ng-packs/packages/account/src/lib/components/register/register.component.html @@ -16,7 +16,13 @@ 'AbpAccount::Login' | abpLocalization }} -
+
* = this.store.selectSnapshot( ConfigState.getSettings('Identity.Password'), ); @@ -67,7 +83,7 @@ export class RegisterComponent implements OnInit { username: ['', [required, maxLength(255)]], password: [ '', - [required, validatePassword(passwordRulesArr), minLength(requiredLength), maxLength(32)], + [required, validatePassword(passwordRulesArr), minLength(requiredLength), maxLength(128)], ], email: ['', [required, email]], }); @@ -85,25 +101,10 @@ export class RegisterComponent implements OnInit { appName: 'Angular', } as RegisterRequest; - const tenant = this.store.selectSnapshot(SessionState.getTenant); - this.accountService .register(newUser) .pipe( - switchMap(() => - from( - this.oauthService.fetchTokenUsingPasswordFlow( - newUser.userName, - newUser.password, - new HttpHeaders({ - ...(tenant && tenant.id && { __tenant: tenant.id }), - }), - ), - ), - ), - switchMap(() => this.store.dispatch(new GetAppConfiguration())), - tap(() => this.store.dispatch(new Navigate(['/']))), - take(1), + switchMap(() => this.authService.login(newUser.userName, newUser.password)), catchError(err => { this.toasterService.error( snq(() => err.error.error_description) || diff --git a/npm/ng-packs/packages/core/src/lib/actions/session.actions.ts b/npm/ng-packs/packages/core/src/lib/actions/session.actions.ts index 7a89be4de3..463e9a202d 100644 --- a/npm/ng-packs/packages/core/src/lib/actions/session.actions.ts +++ b/npm/ng-packs/packages/core/src/lib/actions/session.actions.ts @@ -8,3 +8,11 @@ export class SetTenant { static readonly type = '[Session] Set Tenant'; constructor(public payload: ABP.BasicItem) {} } +export class ModifyOpenedTabCount { + static readonly type = '[Session] Modify Opened Tab Count'; + constructor(public operation: 'increase' | 'decrease') {} +} +export class SetRemember { + static readonly type = '[Session] Set Remember'; + constructor(public payload: boolean) {} +} diff --git a/npm/ng-packs/packages/core/src/lib/core.module.ts b/npm/ng-packs/packages/core/src/lib/core.module.ts index a3fc08c7e2..e89da17f29 100644 --- a/npm/ng-packs/packages/core/src/lib/core.module.ts +++ b/npm/ng-packs/packages/core/src/lib/core.module.ts @@ -6,7 +6,7 @@ import { RouterModule } from '@angular/router'; import { NgxsRouterPluginModule } from '@ngxs/router-plugin'; import { NgxsStoragePluginModule } from '@ngxs/storage-plugin'; import { NgxsModule, NGXS_PLUGINS } from '@ngxs/store'; -import { OAuthModule } from 'angular-oauth2-oidc'; +import { OAuthModule, OAuthStorage } from 'angular-oauth2-oidc'; import { AbstractNgModelComponent } from './abstracts/ng-model.component'; import { DynamicLayoutComponent } from './components/dynamic-layout.component'; import { RouterOutletComponent } from './components/router-outlet.component'; @@ -34,12 +34,15 @@ import { ReplaceableComponentsState } from './states/replaceable-components.stat import { InitDirective } from './directives/init.directive'; import { ReplaceableTemplateDirective } from './directives/replaceable-template.directive'; +export function storageFactory(): OAuthStorage { + return localStorage; +} @NgModule({ imports: [ NgxsModule.forFeature([ReplaceableComponentsState, ProfileState, SessionState, ConfigState]), NgxsRouterPluginModule.forRoot(), NgxsStoragePluginModule.forRoot({ key: ['SessionState'] }), - OAuthModule.forRoot(), + OAuthModule, CommonModule, HttpClientModule, FormsModule, @@ -127,6 +130,8 @@ export class CoreModule { deps: [Injector], useFactory: localeInitializer, }, + ...OAuthModule.forRoot().providers, + { provide: OAuthStorage, useFactory: storageFactory }, ], }; } diff --git a/npm/ng-packs/packages/core/src/lib/models/session.ts b/npm/ng-packs/packages/core/src/lib/models/session.ts index 8eba0cb36c..110fad53f5 100644 --- a/npm/ng-packs/packages/core/src/lib/models/session.ts +++ b/npm/ng-packs/packages/core/src/lib/models/session.ts @@ -4,5 +4,12 @@ export namespace Session { export interface State { language: string; tenant: ABP.BasicItem; + sessionDetail: SessionDetail; + } + + export interface SessionDetail { + openedTabCount: number; + lastExitTime: number; + remember: boolean; } } diff --git a/npm/ng-packs/packages/core/src/lib/services/auth.service.ts b/npm/ng-packs/packages/core/src/lib/services/auth.service.ts new file mode 100644 index 0000000000..0fc69f62ff --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/services/auth.service.ts @@ -0,0 +1,51 @@ +import { HttpHeaders } from '@angular/common/http'; +import { Inject, Injectable, Optional } from '@angular/core'; +import { Navigate } from '@ngxs/router-plugin'; +import { Store } from '@ngxs/store'; +import { OAuthService } from 'angular-oauth2-oidc'; +import { from, Observable } from 'rxjs'; +import { switchMap, tap, take } from 'rxjs/operators'; +import snq from 'snq'; +import { GetAppConfiguration } from '../actions/config.actions'; +import { SessionState } from '../states/session.state'; +import { RestService } from './rest.service'; +import { ConfigState } from '../states/config.state'; + +@Injectable({ + providedIn: 'root', +}) +export class AuthService { + constructor( + private rest: RestService, + private oAuthService: OAuthService, + private store: Store, + @Optional() @Inject('ACCOUNT_OPTIONS') private options: any, + ) {} + + login(username: string, password: string): Observable { + const tenant = this.store.selectSnapshot(SessionState.getTenant); + + this.oAuthService.configure( + this.store.selectSnapshot(ConfigState.getOne('environment')).oAuthConfig, + ); + + return from(this.oAuthService.loadDiscoveryDocument()).pipe( + switchMap(() => + from( + this.oAuthService.fetchTokenUsingPasswordFlow( + username, + password, + new HttpHeaders({ ...(tenant && tenant.id && { __tenant: tenant.id }) }), + ), + ), + ), + switchMap(() => this.store.dispatch(new GetAppConfiguration())), + tap(() => { + const redirectUrl = + snq(() => window.history.state.redirectUrl) || (this.options || {}).redirectUrl || '/'; + this.store.dispatch(new Navigate([redirectUrl])); + }), + take(1), + ); + } +} diff --git a/npm/ng-packs/packages/core/src/lib/services/index.ts b/npm/ng-packs/packages/core/src/lib/services/index.ts index 6b90b997cc..e7f7f9b985 100644 --- a/npm/ng-packs/packages/core/src/lib/services/index.ts +++ b/npm/ng-packs/packages/core/src/lib/services/index.ts @@ -1,4 +1,5 @@ export * from './application-configuration.service'; +export * from './auth.service'; export * from './config-state.service'; export * from './lazy-load.service'; export * from './localization.service'; diff --git a/npm/ng-packs/packages/core/src/lib/services/session-state.service.ts b/npm/ng-packs/packages/core/src/lib/services/session-state.service.ts index 88b8f2df9b..2875e02824 100644 --- a/npm/ng-packs/packages/core/src/lib/services/session-state.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/session-state.service.ts @@ -1,8 +1,12 @@ import { Injectable } from '@angular/core'; import { Store } from '@ngxs/store'; +import { + SetLanguage, + SetRemember, + SetTenant, + ModifyOpenedTabCount, +} from '../actions/session.actions'; import { SessionState } from '../states'; -import { ABP } from '../models'; -import { SetLanguage, SetTenant } from '../actions'; @Injectable({ providedIn: 'root', @@ -18,6 +22,10 @@ export class SessionStateService { return this.store.selectSnapshot(SessionState.getTenant); } + getSessionDetail() { + return this.store.selectSnapshot(SessionState.getSessionDetail); + } + dispatchSetLanguage(...args: ConstructorParameters) { return this.store.dispatch(new SetLanguage(...args)); } @@ -25,4 +33,12 @@ export class SessionStateService { dispatchSetTenant(...args: ConstructorParameters) { return this.store.dispatch(new SetTenant(...args)); } + + dispatchSetRemember(...args: ConstructorParameters) { + return this.store.dispatch(new SetRemember(...args)); + } + + dispatchModifyOpenedTabCount(...args: ConstructorParameters) { + return this.store.dispatch(new ModifyOpenedTabCount(...args)); + } } diff --git a/npm/ng-packs/packages/core/src/lib/states/session.state.ts b/npm/ng-packs/packages/core/src/lib/states/session.state.ts index 06372c8e7d..7ceaa4a1bc 100644 --- a/npm/ng-packs/packages/core/src/lib/states/session.state.ts +++ b/npm/ng-packs/packages/core/src/lib/states/session.state.ts @@ -1,14 +1,29 @@ -import { Action, Selector, State, StateContext } from '@ngxs/store'; -import { from } from 'rxjs'; -import { switchMap } from 'rxjs/operators'; +import { + Action, + Selector, + State, + StateContext, + Store, + NgxsOnInit, + Actions, + ofActionSuccessful, +} from '@ngxs/store'; +import { from, fromEvent } from 'rxjs'; +import { switchMap, take } from 'rxjs/operators'; import { GetAppConfiguration } from '../actions/config.actions'; -import { SetLanguage, SetTenant } from '../actions/session.actions'; +import { + SetLanguage, + SetTenant, + ModifyOpenedTabCount, + SetRemember, +} from '../actions/session.actions'; import { ABP, Session } from '../models'; import { LocalizationService } from '../services/localization.service'; +import { OAuthService } from 'angular-oauth2-oidc'; @State({ name: 'SessionState', - defaults: {} as Session.State, + defaults: { sessionDetail: { openedTabCount: 0 } } as Session.State, }) export class SessionState { @Selector() @@ -21,7 +36,42 @@ export class SessionState { return tenant; } - constructor(private localizationService: LocalizationService) {} + @Selector() + static getSessionDetail({ sessionDetail }: Session.State): Session.SessionDetail { + return sessionDetail; + } + + constructor( + private localizationService: LocalizationService, + private oAuthService: OAuthService, + private store: Store, + private actions: Actions, + ) { + actions + .pipe(ofActionSuccessful(GetAppConfiguration)) + .pipe(take(1)) + .subscribe(() => { + const { sessionDetail } = this.store.selectSnapshot(SessionState) || { sessionDetail: {} }; + + const fiveMinutesBefore = new Date().valueOf() - 5 * 60 * 1000; + + if ( + sessionDetail.lastExitTime && + sessionDetail.openedTabCount === 0 && + this.oAuthService.hasValidAccessToken() && + sessionDetail.remember === false && + sessionDetail.lastExitTime < fiveMinutesBefore + ) { + this.oAuthService.logOut(); + } + + this.store.dispatch(new ModifyOpenedTabCount('increase')); + + fromEvent(window, 'beforeunload').subscribe(() => { + this.store.dispatch(new ModifyOpenedTabCount('decrease')); + }); + }); + } @Action(SetLanguage) setLanguage({ patchState, dispatch }: StateContext, { payload }: SetLanguage) { @@ -40,4 +90,48 @@ export class SessionState { tenant: payload, }); } + + @Action(SetRemember) + setRemember( + { getState, patchState }: StateContext, + { payload: remember }: SetRemember, + ) { + const { sessionDetail } = getState(); + + patchState({ + sessionDetail: { + ...sessionDetail, + remember, + }, + }); + } + + @Action(ModifyOpenedTabCount) + modifyOpenedTabCount( + { getState, patchState }: StateContext, + { operation }: ModifyOpenedTabCount, + ) { + // tslint:disable-next-line: prefer-const + let { openedTabCount, lastExitTime, ...detail } = + getState().sessionDetail || ({ openedTabCount: 0 } as Session.SessionDetail); + + if (operation === 'increase') { + openedTabCount++; + } else if (operation === 'decrease') { + openedTabCount--; + lastExitTime = new Date().valueOf(); + } + + if (!openedTabCount || openedTabCount < 0) { + openedTabCount = 0; + } + + patchState({ + sessionDetail: { + openedTabCount, + lastExitTime, + ...detail, + }, + }); + } } diff --git a/npm/ng-packs/packages/core/src/lib/tests/config.plugin.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/config.plugin.spec.ts index a5bc046012..aa0e0f553f 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/config.plugin.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/config.plugin.spec.ts @@ -10,6 +10,7 @@ import { ABP } from '../models'; import { ConfigPlugin, NGXS_CONFIG_PLUGIN_OPTIONS } from '../plugins'; import { ConfigState } from '../states'; import { addAbpRoutes } from '../utils'; +import { OAuthModule } from 'angular-oauth2-oidc'; addAbpRoutes([ { @@ -323,6 +324,7 @@ describe('ConfigPlugin', () => { service: ConfigPlugin, imports: [ CoreModule, + OAuthModule.forRoot(), NgxsModule.forRoot([]), RouterTestingModule.withRoutes([ { diff --git a/npm/ng-packs/packages/core/src/lib/tests/session-state.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/session-state.service.spec.ts index 8bca7d1ae3..cff6f9e239 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/session-state.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/session-state.service.spec.ts @@ -3,13 +3,17 @@ import { SessionStateService } from '../services/session-state.service'; import { SessionState } from '../states/session.state'; import { Store } from '@ngxs/store'; import * as SessionActions from '../actions'; +import { OAuthService } from 'angular-oauth2-oidc'; describe('SessionStateService', () => { let service: SessionStateService; let spectator: SpectatorService; let store: SpyObject; - const createService = createServiceFactory({ service: SessionStateService, mocks: [Store] }); + const createService = createServiceFactory({ + service: SessionStateService, + mocks: [Store, OAuthService], + }); beforeEach(() => { spectator = createService(); service = spectator.service; diff --git a/npm/ng-packs/packages/core/src/lib/tests/session.state.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/session.state.spec.ts index 412ed8d9ab..cdf616e5d2 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/session.state.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/session.state.spec.ts @@ -1,9 +1,11 @@ import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; import { Session } from '../models/session'; -import { LocalizationService } from '../services'; +import { LocalizationService, AuthService } from '../services'; import { SessionState } from '../states'; import { GetAppConfiguration } from '../actions/config.actions'; -import { of } from 'rxjs'; +import { of, Subject } from 'rxjs'; +import { Store, Actions } from '@ngxs/store'; +import { OAuthService } from 'angular-oauth2-oidc'; export class DummyClass {} @@ -18,12 +20,12 @@ describe('SessionState', () => { const createService = createServiceFactory({ service: DummyClass, - mocks: [LocalizationService], + mocks: [LocalizationService, Store, Actions, OAuthService], }); beforeEach(() => { spectator = createService(); - state = new SessionState(spectator.get(LocalizationService)); + state = new SessionState(spectator.get(LocalizationService), null, null, new Subject()); }); describe('#getLanguage', () => { diff --git a/npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts b/npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts index d33430d98c..427989af59 100644 --- a/npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts +++ b/npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts @@ -144,7 +144,7 @@ export class UsersComponent implements OnInit { const passwordValidators = [ validatePassword(this.passwordRulesArr), Validators.minLength(this.requiredPasswordLength), - Validators.maxLength(32), + Validators.maxLength(128), ]; this.form.addControl('password', new FormControl('', [...passwordValidators])); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/confirmation.service.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/confirmation.service.spec.ts index 361ec0c020..c810df9367 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/confirmation.service.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/confirmation.service.spec.ts @@ -6,6 +6,7 @@ import { NgxsModule } from '@ngxs/store'; import { MessageService } from 'primeng/components/common/messageservice'; import { ConfirmationService } from '../services/confirmation.service'; import { ThemeSharedModule } from '../theme-shared.module'; +import { OAuthModule, OAuthService } from 'angular-oauth2-oidc'; @Component({ selector: 'abp-dummy', @@ -24,6 +25,7 @@ describe('ConfirmationService', () => { component: DummyComponent, imports: [CoreModule, ThemeSharedModule.forRoot(), NgxsModule.forRoot(), RouterTestingModule], providers: [MessageService], + mocks: [OAuthService], }); beforeEach(() => { 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 88ae00a874..1be1e31d1c 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 @@ -9,8 +9,12 @@ 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'; +import { OAuthModule, OAuthService } from 'angular-oauth2-oidc'; -@Component({ selector: 'abp-dummy', template: 'dummy works! ' }) +@Component({ + selector: 'abp-dummy', + template: 'dummy works! ', +}) class DummyComponent { constructor(public errorHandler: ErrorHandler) {} } @@ -21,6 +25,7 @@ describe('ErrorHandler', () => { const createComponent = createRoutingFactory({ component: DummyComponent, imports: [CoreModule, ThemeSharedModule.forRoot(), NgxsModule.forRoot([])], + mocks: [OAuthService], stubsEnabled: false, routes: [ { path: '', component: DummyComponent }, @@ -38,33 +43,53 @@ describe('ErrorHandler', () => { it('should display the error component when server error occurs', () => { store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 500 }))); - expect(document.querySelector('.error-template')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError500.title); - expect(document.querySelector('.error-details')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError500.details); + expect(document.querySelector('.error-template')).toHaveText( + DEFAULT_ERROR_MESSAGES.defaultError500.title, + ); + 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 }))); - expect(document.querySelector('.error-template')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError403.title); - expect(document.querySelector('.error-details')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError403.details); + 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' }))); - expect(document.querySelector('.error-template')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError.title); + store.dispatch( + new RestOccurError(new HttpErrorResponse({ status: 0, statusText: 'Unknown Error' })), + ); + expect(document.querySelector('.error-template')).toHaveText( + DEFAULT_ERROR_MESSAGES.defaultError.title, + ); }); it('should display the confirmation when not found error occurs', () => { store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 404 }))); spectator.detectChanges(); - expect(spectator.query('.abp-confirm-summary')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError404.title); - expect(spectator.query('.abp-confirm-body')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError404.details); + expect(spectator.query('.abp-confirm-summary')).toHaveText( + DEFAULT_ERROR_MESSAGES.defaultError404.title, + ); + expect(spectator.query('.abp-confirm-body')).toHaveText( + DEFAULT_ERROR_MESSAGES.defaultError404.details, + ); }); it('should display the confirmation when default error occurs', () => { store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 412 }))); spectator.detectChanges(); - expect(spectator.query('.abp-confirm-summary')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError.title); - expect(spectator.query('.abp-confirm-body')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError.details); + expect(spectator.query('.abp-confirm-summary')).toHaveText( + DEFAULT_ERROR_MESSAGES.defaultError.title, + ); + expect(spectator.query('.abp-confirm-body')).toHaveText( + DEFAULT_ERROR_MESSAGES.defaultError.details, + ); }); it('should display the confirmation when authenticated error occurs', async () => { @@ -110,7 +135,8 @@ describe('ErrorHandler', () => { @Component({ selector: 'abp-dummy-error', - template: '

{{errorStatus}}

', + template: + '

{{errorStatus}}

', }) class DummyErrorComponent { errorStatus; @@ -130,12 +156,16 @@ describe('ErrorHandler with custom error component', () => { imports: [ CoreModule, ThemeSharedModule.forRoot({ - httpErrorConfig: { errorScreen: { component: DummyErrorComponent, forWhichErrors: [401, 403, 404, 500] } }, + httpErrorConfig: { + errorScreen: { component: DummyErrorComponent, forWhichErrors: [401, 403, 404, 500] }, + }, }), NgxsModule.forRoot([]), ErrorModule, ], + mocks: [OAuthService], stubsEnabled: false, + routes: [ { path: '', component: DummyComponent }, { path: 'account/login', component: RouterOutletComponent }, diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/toaster.service.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/toaster.service.spec.ts index 149ecd1fad..93272e4d30 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/toaster.service.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/toaster.service.spec.ts @@ -6,6 +6,7 @@ import { NgxsModule } from '@ngxs/store'; import { MessageService } from 'primeng/components/common/messageservice'; import { ToasterService } from '../services/toaster.service'; import { ThemeSharedModule } from '../theme-shared.module'; +import { OAuthService } from 'angular-oauth2-oidc'; @Component({ selector: 'abp-dummy', @@ -24,6 +25,7 @@ describe('ToasterService', () => { component: DummyComponent, imports: [CoreModule, ThemeSharedModule.forRoot(), NgxsModule.forRoot(), RouterTestingModule], providers: [MessageService], + mocks: [OAuthService], }); beforeEach(() => { @@ -67,10 +69,9 @@ describe('ToasterService', () => { { summary: 'summary2', detail: 'detail2' }, ]); spectator.detectChanges(); - expect(spectator.queryAll('div.ui-toast-summary').map(node => node.textContent.trim())).toEqual([ - 'summary1', - 'summary2', - ]); + expect( + spectator.queryAll('div.ui-toast-summary').map(node => node.textContent.trim()), + ).toEqual(['summary1', 'summary2']); expect(spectator.queryAll('div.ui-toast-detail').map(node => node.textContent.trim())).toEqual([ 'detail1', 'detail2',