Skip to content

Commit 88db364

Browse files
authored
Merge branch 'master' into TT-20-Calculate-time-difference-when-new-entries-added
2 parents 7f46d2b + 03cd1f9 commit 88db364

30 files changed

+860
-459
lines changed

package-lock.json

Lines changed: 260 additions & 282 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "time-tracker",
3-
"version": "1.35.0",
3+
"version": "1.38.1",
44
"scripts": {
55
"preinstall": "npx npm-force-resolutions",
66
"ng": "ng",
@@ -42,6 +42,7 @@
4242
"minimist": "^1.2.5",
4343
"moment": "^2.25.3",
4444
"msal": "^1.2.1",
45+
"ngrx-store-localstorage": "^11.0.0",
4546
"ngx-cookie-service": "^11.0.2",
4647
"ngx-mask": "^9.1.2",
4748
"ngx-material-timepicker": "^5.5.3",
@@ -88,7 +89,7 @@
8889
"popper.js": "^1.16.0",
8990
"prettier": "^2.0.2",
9091
"protractor": "^7.0.0",
91-
"semantic-release": "^17.3.0",
92+
"semantic-release": "^17.4.2",
9293
"ts-node": "~8.3.0",
9394
"tslint": "~6.1.0",
9495
"typescript": "4.0.5"

src/app/app-routing.module.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { AdminGuard } from './guards/admin-guard/admin-guard';
1+
import { AdminGuard } from './guards/admin-guard/admin.guard';
22
import { NgModule } from '@angular/core';
33
import { Routes, RouterModule } from '@angular/router';
44

@@ -26,7 +26,7 @@ const routes: Routes = [
2626
{ path: 'activities-management', component: ActivitiesManagementComponent },
2727
{ path: 'customers-management', canActivate: [AdminGuard], component: CustomerComponent },
2828
{ path: 'users', canActivate: [AdminGuard], component: UsersComponent },
29-
{ path: 'technology-report', canActivate: [AdminGuard, TechnologiesReportGuard], component: TechnologyReportComponent},
29+
{ path: 'technology-report', canActivate: [AdminGuard, TechnologiesReportGuard], component: TechnologyReportComponent },
3030
{ path: '', pathMatch: 'full', redirectTo: 'time-clock' },
3131
],
3232
},
@@ -37,4 +37,4 @@ const routes: Routes = [
3737
imports: [RouterModule.forRoot(routes)],
3838
exports: [RouterModule],
3939
})
40-
export class AppRoutingModule {}
40+
export class AppRoutingModule { }

src/app/guards/admin-guard/admin-guard.ts

Lines changed: 0 additions & 20 deletions
This file was deleted.
Lines changed: 92 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,129 @@
11
import { inject, TestBed } from '@angular/core/testing';
22
import { Router } from '@angular/router';
33
import { RouterTestingModule } from '@angular/router/testing';
4-
4+
import { of } from 'rxjs';
5+
import { skip, take } from 'rxjs/operators';
6+
import { FeatureSwitchGroupService } from 'src/app/modules/shared/feature-toggles/switch-group/feature-switch-group.service';
7+
import { UserInfoService } from 'src/app/modules/user/services/user-info.service';
58
import { AzureAdB2CService } from '../../modules/login/services/azure.ad.b2c.service';
6-
import { AdminGuard } from './admin-guard';
9+
import { AdminGuard } from './admin.guard';
710

811
describe('AdminGuard', () => {
9-
1012
let adminGuard: AdminGuard;
1113
let azureAdB2CService: AzureAdB2CService;
12-
14+
let userInfoService: UserInfoService;
15+
let featureSwitchGroupService: FeatureSwitchGroupService;
1316
const azureAdB2CServiceStub = {
1417
isLogin() {
1518
return true;
1619
},
1720
isAdmin() {
1821
return true;
19-
}
22+
},
23+
};
24+
25+
const userInfoServiceStub = {
26+
isAdmin: () => of(false),
27+
};
28+
29+
const featureSwitchGroupServiceStub = {
30+
isActivated: () => of(false),
2031
};
2132

2233
beforeEach(() => {
2334
TestBed.configureTestingModule({
2435
imports: [RouterTestingModule],
2536
providers: [
26-
{ providers: AzureAdB2CService, useValue: azureAdB2CServiceStub },
27-
]
37+
{ provide: AzureAdB2CService, useValue: azureAdB2CServiceStub },
38+
{ provide: UserInfoService, useValue: userInfoServiceStub },
39+
{ provide: FeatureSwitchGroupService, useValue: featureSwitchGroupServiceStub },
40+
],
2841
});
2942
adminGuard = TestBed.inject(AdminGuard);
3043
azureAdB2CService = TestBed.inject(AzureAdB2CService);
44+
userInfoService = TestBed.inject(UserInfoService);
45+
featureSwitchGroupService = TestBed.inject(FeatureSwitchGroupService);
3146
});
3247

3348
it('should be created', () => {
3449
expect(adminGuard).toBeTruthy();
3550
});
3651

37-
it('can activate the route when user is logged-in', () => {
38-
spyOn(azureAdB2CService, 'isAdmin').and.returnValue(true);
52+
const roleParams = [{ bool: false }, { bool: true }];
53+
roleParams.map((param) => {
54+
it(`isAdminBasedInRole return ${param.bool}`, () => {
55+
spyOn(azureAdB2CService, 'isAdmin').and.returnValue(param.bool);
56+
57+
adminGuard.isAdminBasedInRole().subscribe((enabled) => {
58+
expect(azureAdB2CService.isAdmin).toHaveBeenCalled();
59+
expect(enabled).toBe(param.bool);
60+
});
61+
});
62+
});
63+
64+
const groupParams = [{ bool: false }, { bool: true }];
65+
groupParams.map((param) => {
66+
it(`isAdminBasedInGroup return ${param.bool}`, () => {
67+
spyOn(userInfoService, 'isAdmin').and.returnValue(of(param.bool));
68+
69+
adminGuard.isAdminBasedInGroup().subscribe((enabled) => {
70+
expect(userInfoService.isAdmin).toHaveBeenCalled();
71+
expect(enabled).toBe(param.bool);
72+
});
73+
});
74+
});
75+
76+
const switchToggleParams = [
77+
{ switchGroup: false, chosen: 'isAdminBasedInRole', isAdmin: true },
78+
{ switchGroup: true, chosen: 'isAdminBasedInGroup', isAdmin: false },
79+
];
80+
switchToggleParams.map((param) => {
81+
it(`on switchGroup ${param.switchGroup}, ${param.chosen} should be chosen`, () => {
82+
const switchGroup$ = of(param.switchGroup);
83+
84+
spyOn(featureSwitchGroupService, 'isActivated').and.returnValue(switchGroup$);
85+
86+
const canActivate = adminGuard.canActivate();
3987

40-
const canActivate = adminGuard.canActivate();
88+
featureSwitchGroupService.isActivated().pipe(take(1));
4189

42-
expect(azureAdB2CService.isAdmin).toHaveBeenCalled();
43-
expect(canActivate).toEqual(true);
90+
canActivate.subscribe((enabled) => {
91+
expect(featureSwitchGroupService.isActivated).toHaveBeenCalled();
92+
expect(enabled).toBe(param.isAdmin);
93+
});
94+
});
4495
});
4596

46-
it('can not active the route and is redirected to login if user is not logged-in', inject([Router], (router: Router) => {
47-
spyOn(azureAdB2CService, 'isAdmin').and.returnValue(false);
48-
spyOn(router, 'navigate').and.stub();
97+
const navigateParams = [
98+
{ switchGroup: false, chosen: 'activate the route', isAdmin: true },
99+
{ switchGroup: false, chosen: 'redirect to /login', isAdmin: false },
100+
{ switchGroup: true, chosen: 'activate the route', isAdmin: true },
101+
{ switchGroup: true, chosen: 'redirect to /login', isAdmin: false },
102+
];
103+
navigateParams.map((param) => {
104+
it(`on isAdmin: ${param.isAdmin} with toggleSwitch: ${param.switchGroup}, should ${param.chosen} `, inject(
105+
[Router],
106+
(router: Router) => {
107+
const switchGroup$ = of(param.switchGroup);
108+
const isAdmin$ = of(param.isAdmin);
49109

50-
const canActivate = adminGuard.canActivate();
110+
spyOn(featureSwitchGroupService, 'isActivated').and.returnValue(switchGroup$);
111+
spyOn(adminGuard, 'isAdminBasedInRole').and.returnValue(isAdmin$);
112+
spyOn(adminGuard, 'isAdminBasedInGroup').and.returnValue(isAdmin$);
113+
spyOn(router, 'navigate').and.stub();
51114

52-
expect(azureAdB2CService.isAdmin).toHaveBeenCalled();
53-
expect(canActivate).toEqual(false);
54-
expect(router.navigate).toHaveBeenCalledWith(['login']);
55-
}));
115+
const canActivate = adminGuard.canActivate();
56116

117+
canActivate.subscribe((enabled) => {
118+
expect(featureSwitchGroupService.isActivated).toHaveBeenCalled();
119+
if (!enabled) {
120+
expect(router.navigate).toHaveBeenCalledWith(['login']);
121+
} else {
122+
expect(router.navigate).not.toHaveBeenCalled();
123+
expect(enabled).toBeTrue();
124+
}
125+
});
126+
}
127+
));
128+
});
57129
});
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Injectable } from '@angular/core';
2+
import { Router, CanActivate } from '@angular/router';
3+
import { Observable, of } from 'rxjs';
4+
import { map, mergeMap } from 'rxjs/operators';
5+
import { FeatureSwitchGroupService } from 'src/app/modules/shared/feature-toggles/switch-group/feature-switch-group.service';
6+
import { UserInfoService } from 'src/app/modules/user/services/user-info.service';
7+
import { AzureAdB2CService } from '../../modules/login/services/azure.ad.b2c.service';
8+
9+
@Injectable({
10+
providedIn: 'root',
11+
})
12+
export class AdminGuard implements CanActivate {
13+
constructor(
14+
private azureAdB2CService: AzureAdB2CService,
15+
private router: Router,
16+
private userInfoService: UserInfoService,
17+
private featureSwitchGroup: FeatureSwitchGroupService
18+
) {}
19+
20+
canActivate(): Observable<boolean> {
21+
return this.featureSwitchGroup.isActivated().pipe(
22+
mergeMap((enabled: boolean) => {
23+
return enabled ? this.isAdminBasedInGroup() : this.isAdminBasedInRole();
24+
}),
25+
map((isAdmin: boolean): boolean => {
26+
if (!isAdmin) {
27+
this.router.navigate(['login']);
28+
}
29+
return isAdmin;
30+
})
31+
);
32+
}
33+
34+
isAdminBasedInRole(): Observable<boolean> {
35+
return of(this.azureAdB2CService.isAdmin());
36+
}
37+
38+
isAdminBasedInGroup(): Observable<boolean> {
39+
return this.userInfoService.isAdmin();
40+
}
41+
}

src/app/modules/customer-management/components/customer-info/components/create-customer/create-customer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ export class CreateCustomerComponent implements OnInit, OnDestroy {
7070
this.store.dispatch(new LoadProjectTypes(customerData.id));
7171
this.store.dispatch(new LoadCustomerProjects(customerData.id));
7272
this.changeValueAreTabsActives.emit(true);
73-
this.hasChangedEvent.emit(this.hasChange = false);
7473
this.customerForm.setValue({
7574
name: customerData.name,
7675
description: customerData.description,
@@ -81,6 +80,7 @@ export class CreateCustomerComponent implements OnInit, OnDestroy {
8180
this.markTabsAsInactive();
8281
this.customerForm.reset();
8382
}
83+
this.hasChangedEvent.emit((this.hasChange = false));
8484
}
8585

8686
markTabsAsInactive() {

src/app/modules/customer-management/components/customer-info/components/customer-list/customer-list.component.spec.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ import {
88
CustomerState,
99
DeleteCustomer,
1010
LoadCustomers,
11+
ResetCustomerToEdit,
1112
SetCustomerToEdit,
1213
} from 'src/app/modules/customer-management/store';
1314
import { DataTablesModule } from 'angular-datatables';
1415
import { ActionsSubject } from '@ngrx/store';
15-
import { ResetProjectToEdit } from '../../../projects/components/store/project.actions';
16-
import { ResetProjectTypeToEdit } from '../../../projects-type/store';
16+
import { ResetProjectToEdit, SetProjectToEdit } from '../../../projects/components/store/project.actions';
17+
import { ResetProjectTypeToEdit, SetProjectTypeToEdit } from '../../../projects-type/store';
1718

1819
describe('CustomerTableListComponent', () => {
1920
let component: CustomerListComponent;
@@ -58,7 +59,6 @@ describe('CustomerTableListComponent', () => {
5859
expect(store.dispatch).toHaveBeenCalledWith(new LoadCustomers());
5960
});
6061

61-
6262
it('Onclick Edit, if there are changes, the modal must be presented ', () => {
6363
component.hasChange = true;
6464
const expectMessage = 'Do you have changes in a client, do you want to discard them?';
@@ -86,6 +86,8 @@ describe('CustomerTableListComponent', () => {
8686

8787
component.editCustomer('1');
8888

89+
expect(store.dispatch).toHaveBeenCalledWith(new SetProjectToEdit(null));
90+
expect(store.dispatch).toHaveBeenCalledWith(new SetProjectTypeToEdit(null));
8991
expect(store.dispatch).toHaveBeenCalledWith(new ResetProjectToEdit());
9092
expect(store.dispatch).toHaveBeenCalledWith(new ResetProjectTypeToEdit());
9193
});
@@ -102,6 +104,17 @@ describe('CustomerTableListComponent', () => {
102104
expect(store.dispatch).toHaveBeenCalledWith(new SetCustomerToEdit('1'));
103105
});
104106

107+
it('when you click close modal, if the idToEdit is equal currentCustomerIdToEdit should dispatch ResetCustomerToEdit', () => {
108+
109+
spyOn(store, 'dispatch');
110+
111+
component.idToEdit = '1';
112+
component.currentCustomerIdToEdit = '1';
113+
component.closeModal();
114+
115+
expect(store.dispatch).toHaveBeenCalledWith(new ResetCustomerToEdit());
116+
});
117+
105118
it('onClick delete, dispatch DeleteCustomer', () => {
106119
spyOn(store, 'dispatch');
107120
component.idToDelete = '1';
@@ -110,6 +123,17 @@ describe('CustomerTableListComponent', () => {
110123
expect(store.dispatch).toHaveBeenCalledWith(new DeleteCustomer('1'));
111124
});
112125

126+
it('onClick delete, if idToDelete is equal to currentCustomerIdToEdit should dispatch ResetCustomerToEdit', () => {
127+
128+
spyOn(store, 'dispatch');
129+
130+
component.idToDelete = '1';
131+
component.currentCustomerIdToEdit = '1';
132+
component.deleteCustomer();
133+
134+
expect(store.dispatch).toHaveBeenCalledWith(new ResetCustomerToEdit());
135+
});
136+
113137
const params = [
114138
{ actionName: 'delete', actionType: CustomerManagementActionTypes.DELETE_CUSTOMER_SUCCESS },
115139
{ actionName: 'update', actionType: CustomerManagementActionTypes.UPDATE_CUSTOMER_SUCCESS },

0 commit comments

Comments
 (0)