diff --git a/package-lock.json b/package-lock.json index 5abb2b1c7..a8033c5e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "time-tracker", - "version": "1.31.6", + "version": "1.31.7", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -6077,6 +6077,15 @@ "p-try": "^2.0.0" } }, + "serialize-javascript": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", + "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, "ssri": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz", @@ -18532,15 +18541,6 @@ } } }, - "serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, "serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", @@ -20015,9 +20015,9 @@ } }, "serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", + "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -21250,9 +21250,9 @@ }, "dependencies": { "serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", + "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", "dev": true, "requires": { "randombytes": "^2.1.0" diff --git a/package.json b/package.json index f5411847c..1ea6a7b0d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "time-tracker", - "version": "1.31.6", + "version": "1.31.7", "scripts": { "preinstall": "npx npm-force-resolutions", "ng": "ng", diff --git a/src/app/modules/time-entries/pages/time-entries.component.html b/src/app/modules/time-entries/pages/time-entries.component.html index 8fabce8a2..9df5c7f0a 100644 --- a/src/app/modules/time-entries/pages/time-entries.component.html +++ b/src/app/modules/time-entries/pages/time-entries.component.html @@ -22,6 +22,7 @@ Date Time in - out Duration + Customer Project Activity @@ -33,6 +34,7 @@ {{ entry.start_date | date: 'MM/dd/yyyy' }} {{ entry.start_date | date: 'HH:mm' }} - {{ entry.end_date | date: 'HH:mm' }} {{ entry.end_date | substractDate: entry.start_date }} + {{ entry.customer_name }} {{ entry.project_name }} {{ entry.activity_name }} diff --git a/src/app/modules/time-entries/pages/time-entries.component.spec.ts b/src/app/modules/time-entries/pages/time-entries.component.spec.ts index 9781720d0..e64fc92f1 100644 --- a/src/app/modules/time-entries/pages/time-entries.component.spec.ts +++ b/src/app/modules/time-entries/pages/time-entries.component.spec.ts @@ -18,6 +18,7 @@ import { TimeEntriesComponent } from './time-entries.component'; import { ActionsSubject } from '@ngrx/store'; import { EntryActionTypes } from './../../time-clock/store/entry.actions'; import { NgxMaterialTimepickerModule } from 'ngx-material-timepicker'; +import { DebugElement } from '@angular/core'; describe('TimeEntriesComponent', () => { type Merged = TechnologyState & ProjectState & EntryState; @@ -57,6 +58,7 @@ describe('TimeEntriesComponent', () => { project_name: 'Time-tracker', start_date: new Date('2020-02-05T15:36:15.887Z'), end_date: new Date('2020-02-05T18:36:15.887Z'), + customer_name: 'ioet Inc.', activity_id: 'development', technologies: ['Angular', 'TypeScript'], description: 'No comments', @@ -375,19 +377,54 @@ describe('TimeEntriesComponent', () => { component.entry = null; component.entryId = null; const lastEntry = { - description : 'testing is fun', - technologies : [], - uri : 'http://testing.is.fun', - activity_id : 'sss', - project_id : 'id', - start_date : new Date(new Date().setHours(0, 0, 0, 0)), - end_date : new Date(new Date().setHours(0, 0, 0, 0)) + description: 'testing is fun', + technologies: [], + uri: 'http://testing.is.fun', + activity_id: 'sss', + project_id: 'id', + start_date: new Date(new Date().setHours(0, 0, 0, 0)), + end_date: new Date(new Date().setHours(0, 0, 0, 0)) }; - state.timeEntriesDataSource.data = [ lastEntry ]; + state.timeEntriesDataSource.data = [lastEntry]; mockEntriesSelector = store.overrideSelector(getTimeEntriesDataSource, state.timeEntriesDataSource); - component.projectSelected({ projectId : 'id'}); + component.projectSelected({ projectId: 'id' }); expect(component.entry).toEqual(lastEntry); })); + it('when the data source is loaded, the table should to show the appropriated column titles', waitForAsync(() => { + component.timeEntriesDataSource$.subscribe(() => { + + fixture.detectChanges(); + + const expectedColumnTitles = [ + 'Date', + 'Time in - out', + 'Duration', + 'Customer', + 'Project', + 'Activity', + '', + ]; + + const columnTitles: string[] = []; + + const HTMLTimeEntriesDebugElement: DebugElement = fixture.debugElement; + const HTMLTimeEntriesElement: HTMLElement = HTMLTimeEntriesDebugElement.nativeElement; + const HTMLTimeEntriesTable = HTMLTimeEntriesElement.querySelector('.table') as HTMLTableElement; + const HTMLTableHead = HTMLTimeEntriesTable.rows[0]; + + Array.from(HTMLTableHead.cells).forEach(columnTitle => { + columnTitles.push(columnTitle.innerText); + }); + expect(expectedColumnTitles).toEqual(columnTitles); + }); + })); + + it('when the data source is loaded, the entry should to have customer_name field', waitForAsync(() => { + component.timeEntriesDataSource$.subscribe(dataSource => { + const entryData = dataSource.data[0]; + expect(entryData.customer_name).toContain('ioet Inc.'); + }); + })); }); diff --git a/src/app/modules/users/components/users-list/users-list.component.html b/src/app/modules/users/components/users-list/users-list.component.html index 71aa110da..b13c0206f 100644 --- a/src/app/modules/users/components/users-list/users-list.component.html +++ b/src/app/modules/users/components/users-list/users-list.component.html @@ -1,22 +1,23 @@ - - - + + + - + - - - + +
User EmailNamesRolesUser EmailNamesRoles
{{ user.email }}{{ user.name }} + {{ user.email }}{{ user.name }}
{ let component: UsersListComponent; let fixture: ComponentFixture; let store: MockStore; - let featureManagerService: FeatureManagerService; const actionSub: ActionsSubject = new ActionsSubject(); const state: UserState = { @@ -39,7 +36,6 @@ describe('UsersListComponent', () => { declarations: [UsersListComponent], providers: [provideMockStore({ initialState: state }), { provide: ActionsSubject, useValue: actionSub }], }).compileComponents(); - featureManagerService = TestBed.inject(FeatureManagerService); }) ); @@ -75,36 +71,6 @@ describe('UsersListComponent', () => { expect(component.users).toEqual(state.data); }); - it('When Component is created, should call the feature toggle method', () => { - spyOn(component, 'isFeatureToggleActivated').and.returnValue(of(true)); - - component.ngOnInit(); - - expect(component.isFeatureToggleActivated).toHaveBeenCalled(); - expect(component.isUserRoleToggleOn).toBe(true); - }); - - const actionsParams = [ - { actionType: UserActionTypes.GRANT_USER_ROLE_SUCCESS }, - { actionType: UserActionTypes.REVOKE_USER_ROLE_SUCCESS }, - ]; - - actionsParams.map((param) => { - it(`When action ${param.actionType} is dispatched should triggered load Users action`, () => { - spyOn(store, 'dispatch'); - - const actionSubject = TestBed.inject(ActionsSubject) as ActionsSubject; - const action = { - type: param.actionType, - payload: state.data, - }; - - actionSubject.next(action); - - expect(store.dispatch).toHaveBeenCalledWith(new LoadUsers()); - }); - }); - const grantRoleTypes = [ { roleId: 'admin', roleValue: 'time-tracker-admin' }, { roleId: 'test', roleValue: 'time-tracker-tester' }, @@ -190,23 +156,7 @@ describe('UsersListComponent', () => { }); }); - const toggleValues = [true, false]; - toggleValues.map((toggleValue) => { - it(`when FeatureToggle is ${toggleValue} should return ${toggleValue}`, () => { - spyOn(featureManagerService, 'isToggleEnabledForUser').and.returnValue(of(toggleValue)); - - const isFeatureToggleActivated: Observable = component.isFeatureToggleActivated(); - - expect(featureManagerService.isToggleEnabledForUser).toHaveBeenCalled(); - isFeatureToggleActivated.subscribe((value) => expect(value).toEqual(toggleValue)); - }); - }); - - /* - TODO: block commented on purpose so that when the tests pass and the Feature toggle is removed, - the table will be rendered again with dtInstance and not with dtOptions - - it('on success load users, the datatable should be reloaded', async () => { + it('on success load users, the datatable should be reloaded', async () => { const actionSubject = TestBed.inject(ActionsSubject); const action = { type: UserActionTypes.LOAD_USERS_SUCCESS, @@ -217,7 +167,7 @@ describe('UsersListComponent', () => { actionSubject.next(action); expect(component.dtElement.dtInstance.then).toHaveBeenCalled(); - });*/ + }); afterEach(() => { component.dtTrigger.unsubscribe(); diff --git a/src/app/modules/users/components/users-list/users-list.component.ts b/src/app/modules/users/components/users-list/users-list.component.ts index 91cfc2e23..bf4df91c4 100644 --- a/src/app/modules/users/components/users-list/users-list.component.ts +++ b/src/app/modules/users/components/users-list/users-list.component.ts @@ -2,11 +2,10 @@ import { AfterViewInit, Component, OnDestroy, OnInit, ViewChild } from '@angular import { ActionsSubject, select, Store } from '@ngrx/store'; import { DataTableDirective } from 'angular-datatables'; import { Observable, Subject, Subscription } from 'rxjs'; -import { delay, filter, map } from 'rxjs/operators'; +import { delay, filter } from 'rxjs/operators'; import { User } from '../../models/users'; import { GrantRoleUser, LoadUsers, RevokeRoleUser, UserActionTypes } from '../../store/user.actions'; import { getIsLoading } from '../../store/user.selectors'; -import { FeatureManagerService } from 'src/app/modules/shared/feature-toggles/feature-toggle-manager.service'; @Component({ selector: 'app-users-list', @@ -22,20 +21,12 @@ export class UsersListComponent implements OnInit, OnDestroy, AfterViewInit { @ViewChild(DataTableDirective, { static: false }) dtElement: DataTableDirective; dtOptions: any = {}; - isUserRoleToggleOn; - constructor( - private store: Store, - private actionsSubject$: ActionsSubject, - private featureManagerService: FeatureManagerService - ) { + constructor(private store: Store, private actionsSubject$: ActionsSubject) { this.isLoading$ = store.pipe(delay(0), select(getIsLoading)); } ngOnInit(): void { - this.isFeatureToggleActivated().subscribe((flag) => { - this.isUserRoleToggleOn = flag; - }); this.store.dispatch(new LoadUsers()); this.loadUsersSubscription = this.actionsSubject$ .pipe(filter((action: any) => action.type === UserActionTypes.LOAD_USERS_SUCCESS)) @@ -43,19 +34,6 @@ export class UsersListComponent implements OnInit, OnDestroy, AfterViewInit { this.users = action.payload; this.rerenderDataTable(); }); - - this.switchRoleSubscription = this.actionsSubject$ - .pipe( - filter( - (action: any) => - action.type === UserActionTypes.GRANT_USER_ROLE_SUCCESS || - action.type === UserActionTypes.REVOKE_USER_ROLE_SUCCESS - ) - ) - .subscribe((action) => { - this.store.dispatch(new LoadUsers()); - this.rerenderDataTable(); - }); } ngAfterViewInit(): void { @@ -83,12 +61,4 @@ export class UsersListComponent implements OnInit, OnDestroy, AfterViewInit { ? this.store.dispatch(new RevokeRoleUser(userId, roleId)) : this.store.dispatch(new GrantRoleUser(userId, roleId)); } - - isFeatureToggleActivated() { - return this.featureManagerService.isToggleEnabledForUser('ui-list-test-users').pipe( - map((enabled) => { - return enabled === true ? true : false; - }) - ); - } }