Commit adee3965 authored by Tifenn Guillas's avatar Tifenn Guillas

Merge branch '156-add-tests-on-auth-module' into 'develop'

Resolve "Add tests on auth module"

Closes #156

See merge request !167
parents 231b95cc dc0c0f16
Pipeline #3748 passed with stages
in 8 minutes and 27 seconds
import * as authActions from './auth.action';
import { UserProfile } from './user-profile.model';
describe('[Auth] Action', () => {
it('should create LoginAction', () => {
const action = new authActions.LoginAction();
expect(action.type).toEqual(authActions.LOGIN);
});
it('should create LogoutAction', () => {
const action = new authActions.LogoutAction();
expect(action.type).toEqual(authActions.LOGOUT);
});
it('should create AuthSuccessAction', () => {
const action = new authActions.AuthSuccessAction();
expect(action.type).toEqual(authActions.AUTH_SUCCESS);
});
it('should create LoadUserProfileSuccessAction', () => {
const profile: UserProfile = { id: 'id', username: 'toto' };
const action = new authActions.LoadUserProfileSuccessAction(profile);
expect(action.type).toEqual(authActions.LOAD_USER_PROFILE_SUCCESS);
expect(action.payload).toEqual(profile);
});
it('should create LoadUserRolesSuccessAction', () => {
const action = new authActions.LoadUserRolesSuccessAction(['toto']);
expect(action.type).toEqual(authActions.LOAD_USER_ROLES_SUCCESS);
expect(action.payload).toEqual(['toto']);
});
it('should create OpenEditProfileAction', () => {
const action = new authActions.OpenEditProfileAction();
expect(action.type).toEqual(authActions.OPEN_EDIT_PROFILE);
});
});
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Action } from '@ngrx/store';
import { UserProfile } from './user-profile.model';
......@@ -7,38 +16,68 @@ export const LOGOUT = '[Auth] Logout';
export const AUTH_SUCCESS = '[Auth] Auth Success';
export const LOAD_USER_PROFILE_SUCCESS = '[Auth] Load User Profile Success';
export const LOAD_USER_ROLES_SUCCESS = '[Auth] Load User Roles Success';
export const OPEN_EDIT_PROFILE = '[Auth] Edit Profile';
export const OPEN_EDIT_PROFILE = '[Auth] Open Edit Profile';
/**
* @class
* @classdesc LoginAction action.
* @readonly
*/
export class LoginAction implements Action {
readonly type = LOGIN;
constructor(public payload: {} = null) { }
}
/**
* @class
* @classdesc LogoutAction action.
* @readonly
*/
export class LogoutAction implements Action {
readonly type = LOGOUT;
constructor(public payload: {} = null) { }
}
/**
* @class
* @classdesc AuthSuccessAction action.
* @readonly
*/
export class AuthSuccessAction implements Action {
readonly type = AUTH_SUCCESS;
constructor(public payload: {} = null) { }
}
/**
* @class
* @classdesc LoadUserProfileSuccessAction action.
* @readonly
*/
export class LoadUserProfileSuccessAction implements Action {
readonly type = LOAD_USER_PROFILE_SUCCESS;
constructor(public payload: UserProfile) { }
}
/**
* @class
* @classdesc LoadUserRolesSuccessAction action.
* @readonly
*/
export class LoadUserRolesSuccessAction implements Action {
readonly type = LOAD_USER_ROLES_SUCCESS;
constructor(public payload: string[]) { }
}
/**
* @class
* @classdesc OpenEditProfileAction action.
* @readonly
*/
export class OpenEditProfileAction implements Action {
readonly type = OPEN_EDIT_PROFILE;
......
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Injectable } from '@angular/core';
import { from } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
import { Effect, Actions, ofType } from '@ngrx/effects';
import { from } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
import { KeycloakService } from 'keycloak-angular';
import * as authActions from './auth.action';
import { environment } from '../../environments/environment';
@Injectable()
/**
* @class
* @classdesc Authentication effects.
*/
export class AuthEffects {
constructor(
private actions$: Actions,
private keycloak: KeycloakService
) { }
/**
* Executes log in.
*/
@Effect({ dispatch: false })
loginAction$ = this.actions$.pipe(
ofType(authActions.LOGIN),
tap(_ => this.keycloak.login())
);
/**
* Executes log out.
*/
@Effect({ dispatch: false })
logoutAction$ = this.actions$.pipe(
ofType(authActions.LOGOUT),
tap(_ => this.keycloak.logout())
);
/**
* Saves user profile and gets user roles.
*/
@Effect()
authSuccessAction$ = this.actions$.pipe(
ofType(authActions.AUTH_SUCCESS),
......@@ -40,6 +63,9 @@ export class AuthEffects {
)
);
/**
* Opens edit profile page.
*/
@Effect({ dispatch: false })
OpenEditProfileAction$ = this.actions$.pipe(
ofType(authActions.OPEN_EDIT_PROFILE),
......
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { NgModule } from '@angular/core';
import { KeycloakAngularModule } from 'keycloak-angular';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { KeycloakAngularModule } from 'keycloak-angular';
import { initializeKeycloakAnis } from './init.keycloak';
import { reducer } from './auth.reducer';
import { AuthEffects } from './auth.effects';
import { initializeKeycloakAnis } from './init.keycloak';
@NgModule({
imports: [
......@@ -18,4 +27,8 @@ import { AuthEffects } from './auth.effects';
initializeKeycloakAnis
]
})
/**
* @class
* @classdesc Authentication module.
*/
export class AuthModule { }
import * as fromAuth from './auth.reducer';
import * as authActions from './auth.action';
import { UserProfile } from './user-profile.model';
describe('[Auth] Reducer', () => {
it('should return init state', () => {
const { initialState } = fromAuth;
const action = {} as authActions.Actions;
const state = fromAuth.reducer(undefined, action);
expect(state).toBe(initialState);
});
it('should set isAuthenticated to true', () => {
const { initialState } = fromAuth;
const action = new authActions.AuthSuccessAction();
const state = fromAuth.reducer(initialState, action);
expect(state.isAuthenticated).toBeTruthy();
expect(state.userProfile).toBeNull();
expect(state.userRoles.length).toEqual(0);
expect(state).not.toEqual(initialState);
});
it('should set userProfile', () => {
const profile: UserProfile = { id: 'id', username: 'toto' };
const { initialState } = fromAuth;
const action = new authActions.LoadUserProfileSuccessAction(profile);
const state = fromAuth.reducer(initialState, action);
expect(state.isAuthenticated).toBeFalsy();
expect(state.userProfile).toEqual(profile);
expect(state.userRoles.length).toEqual(0);
expect(state).not.toEqual(initialState);
});
it('should set userRoles', () => {
const { initialState } = fromAuth;
const action = new authActions.LoadUserRolesSuccessAction(['toto']);
const state = fromAuth.reducer(initialState, action);
expect(state.isAuthenticated).toBeFalsy();
expect(state.userProfile).toBeNull();
expect(state.userRoles.length).toEqual(1);
expect(state.userRoles[0]).toEqual('toto');
expect(state).not.toEqual(initialState);
});
it('should get isAuthenticated', () => {
const action = {} as authActions.Actions;
const state = fromAuth.reducer(undefined, action);
expect(fromAuth.isAuthenticated(state)).toBeFalsy();
});
it('should get userProfile', () => {
const action = {} as authActions.Actions;
const state = fromAuth.reducer(undefined, action);
expect(fromAuth.getUserProfile(state)).toBeNull();
});
it('should get userRoles', () => {
const action = {} as authActions.Actions;
const state = fromAuth.reducer(undefined, action);
expect(fromAuth.getUserRoles(state).length).toEqual(0);
});
});
import * as actions from './auth.action';
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import * as actions from './auth.action';
import { UserProfile } from './user-profile.model';
/**
* Interface for authentication state.
*
* @interface State
*/
export interface State {
isAuthenticated: boolean;
userProfile: UserProfile;
......@@ -14,6 +27,14 @@ export const initialState: State = {
userRoles: []
};
/**
* Reduces state.
*
* @param {State} state - The state.
* @param {actions} action - The action.
*
* @return State
*/
export function reducer(state: State = initialState, action: actions.Actions): State {
switch (action.type) {
case actions.AUTH_SUCCESS:
......@@ -23,19 +44,15 @@ export function reducer(state: State = initialState, action: actions.Actions): S
};
case actions.LOAD_USER_PROFILE_SUCCESS:
const userProfile = action.payload;
return {
...state,
userProfile
userProfile: action.payload
};
case actions.LOAD_USER_ROLES_SUCCESS:
const userRoles = action.payload;
return {
...state,
userRoles
userRoles: action.payload
};
default:
......
import * as authSelector from './auth.selector';
import * as fromAuth from './auth.reducer';
describe('[Auth] Selector', () => {
it('should get isAuthenticated', () => {
const state = { auth: { ...fromAuth.initialState }};
expect(authSelector.isAuthenticated(state)).toBeFalsy();
});
it('should get userProfile', () => {
const state = { auth: { ...fromAuth.initialState }};
expect(authSelector.getUserProfile(state)).toBeNull();
});
it('should get userRoles', () => {
const state = { auth: { ...fromAuth.initialState }};
expect(authSelector.getUserRoles(state).length).toEqual(0);
});
});
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { createSelector, createFeatureSelector } from '@ngrx/store';
import * as auth from './auth.reducer';
......
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { APP_INITIALIZER } from '@angular/core';
import { from } from 'rxjs';
import { KeycloakService, KeycloakEventType } from 'keycloak-angular';
import { Store } from '@ngrx/store';
import { from } from 'rxjs';
import { KeycloakService, KeycloakEventType } from 'keycloak-angular';
import * as keycloakActions from './auth.action';
import * as fromKeycloak from './auth.reducer';
import { environment } from '../../environments/environment';
function initializeKeycloak(keycloak: KeycloakService, store: Store<{ keycloak: fromKeycloak.State }>) {
/**
* Inits Keycloak.
*
* @param {KeycloakService} keycloak - The Keycloak service.
* @param {Store<{ keycloak: fromKeycloak.State }>} store - The Keycloak store.
*
* @return any
*/
function initializeKeycloak(keycloak: KeycloakService, store: Store<{ keycloak: fromKeycloak.State }>): any {
return async () => {
from(keycloak.keycloakEvents$).subscribe(event => {
if (event.type === KeycloakEventType.OnAuthSuccess) {
......@@ -36,5 +54,5 @@ export const initializeKeycloakAnis = {
provide: APP_INITIALIZER,
useFactory: initializeKeycloak,
multi: true,
deps: [ KeycloakService, Store ],
deps: [ KeycloakService, Store ]
};
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Interface for user profile.
*
* @interface UserProfile
*/
export interface UserProfile {
id?: string;
username?: string;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment